This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push: new 33bae98 CAMEL-12699: Allow hystrix EIP to inherit error handler so you can combine Camels error handler for redeliveries with the circuit breaker. 33bae98 is described below commit 33bae980841bd5bf22dcd525a3c530df1571f588 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Sun Aug 5 08:39:23 2018 +0200 CAMEL-12699: Allow hystrix EIP to inherit error handler so you can combine Camels error handler for redeliveries with the circuit breaker. --- camel-core/src/main/docs/eips/hystrix-eip.adoc | 33 +++++++++++++ .../apache/camel/model/ProcessorDefinition.java | 10 +++- .../camel-hystrix/src/main/docs/hystrix.adoc | 4 +- .../processor/HystrixInheritErrorHandlerTest.java | 56 ++++++++++++++++++++++ 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/camel-core/src/main/docs/eips/hystrix-eip.adoc b/camel-core/src/main/docs/eips/hystrix-eip.adoc index 1dae7a3..bb6701b 100644 --- a/camel-core/src/main/docs/eips/hystrix-eip.adoc +++ b/camel-core/src/main/docs/eips/hystrix-eip.adoc @@ -29,6 +29,39 @@ The Hystrix EIP supports 2 options which are listed below: |=== // eip options: END +=== Camel's Error Handler and Hystrix EIP + +By default the Hystrix EIP handles errors by itself. This means if the circuit breaker is open and +the message fails, then Camel's error handler is not reacting also. However from *Camel 2.23* onwards +you can enable Camels error handler with Hystrix by enabling the `inheritErrorHandler` option, as shown: + +[source,java] +---- +// Camel's error handler that will attempt to redeliver the message 3 times +errorHandler(deadLetterChannel("mock:dead").maximumRedeliveries(3).redeliveryDelay(0)); + +from("direct:start") + .to("log:start") + // turn on Camel's error handler on hystrix so it can do redeliveries + .hystrix().inheritErrorHandler(true) + .to("mock:a") + .throwException(new IllegalArgumentException("Forced")) + .end() + .to("log:result") + .to("mock:result"); +---- + +This example is from an unit test, where you can see the Hystrix EIP block has been hardcoded +to always fail by throwning an exception. Because the `inheritErrorHandler` has been enabled, +then Camel's error handler will attempt to call the Hystrix EIP block again. + +That means the `mock:a` endpoint will receive the message again, and a total of 1 + 3 = 4 message +(first time + 3 redeliveries). + +If we turn off the `inheritErrorHandler` option (default) then the Hystrix EIP will only be +executed once because it handled the error itself. + + === Samples Below is an example route showing an Hystrix endpoint that protects against slow operation by falling back to the in-lined fallback route. By default the timeout request is just *1000ms* so the HTTP endpoint has to be fairly quick to succeed. diff --git a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java index 9108d78..535a678 100644 --- a/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java +++ b/camel-core/src/main/java/org/apache/camel/model/ProcessorDefinition.java @@ -312,8 +312,14 @@ public abstract class ProcessorDefinition<Type extends ProcessorDefinition<Type> log.trace("{} is part of OnException so no error handler is applied", defn); // do not use error handler for onExceptions blocks as it will handle errors itself } else if (defn instanceof HystrixDefinition || ProcessorDefinitionHelper.isParentOfType(HystrixDefinition.class, defn, true)) { - log.trace("{} is part of HystrixCircuitBreaker so no error handler is applied", defn); - // do not use error handler for hystrixCircuitBreaker blocks as it will handle errors itself + // do not use error handler for hystrix as it offers circuit breaking with fallback for its outputs + // however if inherit error handler is enabled, we need to wrap an error handler on the hystrix parent + if (inheritErrorHandler != null && inheritErrorHandler && child == null) { + // only wrap the parent (not the children of the hystrix) + wrapChannelInErrorHandler(channel, routeContext, inheritErrorHandler); + } else { + log.trace("{} is part of HystrixCircuitBreaker so no error handler is applied", defn); + } } else if (defn instanceof MulticastDefinition) { // do not use error handler for multicast as it offers fine grained error handlers for its outputs // however if share unit of work is enabled, we need to wrap an error handler on the multicast parent diff --git a/components/camel-hystrix/src/main/docs/hystrix.adoc b/components/camel-hystrix/src/main/docs/hystrix.adoc index 3b9a191..c8d8f3b 100644 --- a/components/camel-hystrix/src/main/docs/hystrix.adoc +++ b/components/camel-hystrix/src/main/docs/hystrix.adoc @@ -1,9 +1,11 @@ -## Hystrix Component +== Hystrix Component *Available as of Camel version 2.18* The hystrix component integrates Netflix Hystrix circuit breaker in Camel routes. +For more details see the Hystrix EIP documentation. + Maven users will need to add the following dependency to their `pom.xml` for this component: diff --git a/components/camel-hystrix/src/test/java/org/apache/camel/component/hystrix/processor/HystrixInheritErrorHandlerTest.java b/components/camel-hystrix/src/test/java/org/apache/camel/component/hystrix/processor/HystrixInheritErrorHandlerTest.java new file mode 100644 index 0000000..b6db3f3 --- /dev/null +++ b/components/camel-hystrix/src/test/java/org/apache/camel/component/hystrix/processor/HystrixInheritErrorHandlerTest.java @@ -0,0 +1,56 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.hystrix.processor; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.Test; + +public class HystrixInheritErrorHandlerTest extends CamelTestSupport { + + @Test + public void testHystrix() throws Exception { + getMockEndpoint("mock:a").expectedMessageCount(3 + 1); + getMockEndpoint("mock:dead").expectedMessageCount(1); + getMockEndpoint("mock:result").expectedMessageCount(0); + + template.sendBody("direct:start", "Hello World"); + + assertMockEndpointsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + errorHandler(deadLetterChannel("mock:dead").maximumRedeliveries(3).redeliveryDelay(0)); + + from("direct:start") + .to("log:start") + // turn on Camel's error handler on hystrix so it can do redeliveries + .hystrix().inheritErrorHandler(true) + .to("mock:a") + .throwException(new IllegalArgumentException("Forced")) + .end() + .to("log:result") + .to("mock:result"); + } + }; + } + +}