Resilience4j EIP
The Resilience4j EIP provides integration with Resilience4j Resilience4j to be used as Circuit Breaker in the Camel routes.
Configuration options
The Resilience4j EIP supports two options which are listed below:
| Name | Description | Default | Type |
|---|---|---|---|
resilience4jConfiguration | Configure the Resilience EIP. When the configuration is complete, use | Resilience4jConfigurationDefinition | |
configuration | Refers to a Resilience configuration (by name) to use for configuring the Resilience EIP. | String |
| See Resilience4j Configuration for all the configuration options on Resilience Circuit Breaker. |
Using Resilience4j EIP
Below is an example route showing a Resilience4j circuit breaker that protects against a downstream HTTP operation with fallback.
-
Java
-
XML
-
YAML
from("direct:start")
.circuitBreaker()
.to("http://fooservice.com/faulty")
.onFallback()
.transform().constant("Fallback message")
.end()
.to("mock:result"); <route>
<from uri="direct:start"/>
<circuitBreaker>
<to uri="http://fooservice.com/faulty"/>
<onFallback>
<transform>
<constant>Fallback message</constant>
</transform>
</onFallback>
</circuitBreaker>
<to uri="mock:result"/>
</route> - route:
from:
uri: direct:start
steps:
- circuitBreaker:
steps:
- to:
uri: http://fooservice.com/faulty
- onFallback:
steps:
- transform:
expression:
constant:
expression: Fallback message
- to:
uri: mock:result In case the calling the downstream HTTP service is failing, and an exception is thrown, then the circuit breaker will react and execute the fallback route instead.
If there is no fallback and the protected processor fails, the circuit breaker will throw an exception. However, when the circuit breaker is open and rejects a call, the message continues routing unchanged (no exception is thrown) unless throwExceptionWhenHalfOpenOrOpenState is enabled.
| For more information about fallback, see onFallback. |
Configuring Resilience4j
You can fine-tune Resilience4j by the many Resilience4j Configuration options.
For example, to use a 2-second execution timeout, you can do as follows:
-
Java
-
XML
-
YAML
from("direct:start")
.circuitBreaker()
// use a 2-second timeout
.resilience4jConfiguration().timeoutEnabled(true).timeoutDuration(2000).end()
.log("Resilience processing start: ${threadName}")
.to("http://fooservice.com/faulty")
.log("Resilience processing end: ${threadName}")
.end()
.log("After Resilience ${body}"); <route>
<from uri="direct:start"/>
<circuitBreaker>
<resilience4jConfiguration timeoutEnabled="true" timeoutDuration="2000"/>
<log message="Resilience processing start: ${threadName}"/>
<to uri="http://fooservice.com/faulty"/>
<log message="Resilience processing end: ${threadName}"/>
</circuitBreaker>
<log message="After Resilience: ${body}"/>
</route> - route:
from:
uri: direct:start
steps:
- circuitBreaker:
steps:
- resilience4jConfiguration:
timeoutDuration: 2000
timeoutEnabled: "true"
- log:
message: "Resilience processing start: ${threadName}"
- to:
uri: http://fooservice.com/faulty
- log:
message: "Resilience processing end: ${threadName}"
- log:
message: "After Resilience ${body}" In this example if calling the downstream service does not return a response within 2 seconds, a timeout is triggered, and the exchange will fail with a TimeoutException.
Asynchronous (non-blocking) processing
By default, the Resilience4j circuit breaker processes exchanges synchronously, blocking the caller thread until the protected operation completes. When asynchronous is enabled, the circuit breaker uses Resilience4j’s CompletionStage-based decorators, which release the caller thread immediately and complete processing asynchronously.
This is most valuable when the downstream processor supports asynchronous processing (e.g., Netty HTTP, Kafka), as it avoids blocking threads while waiting for responses.
-
Java
-
XML
-
YAML
from("direct:start")
.circuitBreaker()
.resilience4jConfiguration()
.asynchronous(true)
.timeoutEnabled(true)
.timeoutDuration(2000)
.end()
.to("http://fooservice.com/faulty")
.onFallback()
.transform().constant("Fallback message")
.end()
.to("mock:result"); <route>
<from uri="direct:start"/>
<circuitBreaker>
<resilience4jConfiguration asynchronous="true" timeoutEnabled="true" timeoutDuration="2000"/>
<to uri="http://fooservice.com/faulty"/>
<onFallback>
<transform>
<constant>Fallback message</constant>
</transform>
</onFallback>
</circuitBreaker>
<to uri="mock:result"/>
</route> - route:
from:
uri: direct:start
steps:
- circuitBreaker:
steps:
- resilience4jConfiguration:
asynchronous: "true"
timeoutDuration: 2000
timeoutEnabled: "true"
- to:
uri: http://fooservice.com/faulty
- onFallback:
steps:
- transform:
expression:
constant:
expression: Fallback message
- to:
uri: mock:result When using asynchronous mode with timeout, the timeoutExecutorService must be a ScheduledExecutorService. If not provided, Camel will automatically create one. If a custom executor is provided that is not a ScheduledExecutorService, the route will fail to start with an IllegalArgumentException. |
Camel’s Error Handler and Circuit Breaker EIP
By default, the Circuit Breaker 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, you can enable Camels error handler with circuit breaker by enabling the inheritErrorHandler option, as shown:
-
Java
-
XML
-
YAML
// 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 circuit breaker so Camel can do redeliveries
.circuitBreaker().inheritErrorHandler(true)
.to("mock:a")
.throwException(new IllegalArgumentException("Forced"))
.end()
.to("log:result")
.to("mock:result"); <route>
<from uri="direct:start"/>
<to uri="log:start"/>
<circuitBreaker inheritErrorHandler="true">
<to uri="mock:a"/>
<throwException exceptionType="java.lang.IllegalArgumentException" message="Forced"/>
</circuitBreaker>
<to uri="log:result"/>
<to uri="mock:result"/>
</route> - route:
from:
uri: direct:start
steps:
- to:
uri: log:start
- circuitBreaker:
inheritErrorHandler: "true"
steps:
- to:
uri: mock:a
- throwException:
exceptionType: "java.lang.IllegalArgumentException"
message: "Forced"
- to:
uri: log:result
- to:
uri: mock:result This example is from a test, where you can see the Circuit Breaker EIP block has been hardcoded to always fail by throwing an exception. Because the inheritErrorHandler has been enabled, then Camel’s error handler will attempt to call the Circuit Breaker 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 Circuit Breaker EIP will only be executed once because it handled the error itself.
Dependencies
| Camel provides the Circuit Breaker EIP in the route model, which allows to plug in different implementations. Resilience4j is one such implementation. |
Maven users will need to add the following dependency to their pom.xml to use this EIP:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-resilience4j</artifactId>
<version>x.x.x</version><!-- use the same version as your Camel core version -->
</dependency>