Spring cloud gateway with Resilience4j circuit breaker
MRomeh
Posted on May 25, 2020
In that post we will cover how to use resilience4j circuit breaker with spring cloud gateway for the back-end services behind the gateway by utilizing the following :
- Resilience4j spring boot starter
- Spring cloud resilience4j circuit breaker
- Spring cloud gateway
In that post we will show the case of how you can mix the usage of the Resilience4j spring boot starter and spring cloud circuit breaker starter so you can configure externally through spring configuration your circuit breakers definitions if you do not want to use the code configuration approach provided by Spring cloud circuit breaker starter through Customizers.
The whole code sample is in github .
First we are create spring cloud gateway application with one micro service route configuration which we will use to enable circuit breaker resilience over it by applying Resilience4j circuit breaker protection over it so we will cover the following points :
- How to enable Resilience4j circuit-breaker in Spring cloud Gateway
- How we can externally configure the defined Resilience4j circuit breaker
- How we can test it by using Mock Server Test containers and the proper setup for it
How to enable Resilience4j circuit-breaker in Spring cloud Gateway:
You need to add the following dependencies to your spring cloud gateway application to enable Resilience4j circuit breaker integration
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>io.github.resilience4j</groupId> <artifactId>resilience4j-spring-boot2</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
We have a sample back-end service rout configuration like the following which will be overridden in our test setup later :
spring: application: name: gateway-service output.ansi.enabled: ALWAYS cloud: gateway: routes: - id: test-service-withResilient4j uri: http://localhost:8091 predicates: - Path=/testService/** filters: - RewritePath=/testService/(?<path>.*), /$\{path}
How we can externally configure the defined Resilience4j circuit breaker :
Now Spring cloud circuit breaker starter allow you to configure your Resilience4j circuit breaker definition through Customizer usage which is code first approach but what If you want to have it externally configured so you can control the configuration externally through distributed configuration service , here Resilience4j spring boot starter come to play which enable the external spring configuration of your circuit-breakers.
Spring boot starter of resilience4j will create CircuitBreakerRegistery bean based into your external configuration then you can inject it to the resilience4j factory of spring cloud starter to enable that integration and that is it!
Resilience4j external configuration will be like :
resilience4j.circuitbreaker: configs: default: slidingWindowSize: 10 minimumNumberOfCalls: 5 permittedNumberOfCallsInHalfOpenState: 3 automaticTransitionFromOpenToHalfOpenEnabled: true waitDurationInOpenState: 2s failureRateThreshold: 50 eventConsumerBufferSize: 10 recordExceptions: - org.springframework.web.client.HttpServerErrorException - java.io.IOException ignoreExceptions: - java.lang.IllegalStateException shared: slidingWindowSize: 100 permittedNumberOfCallsInHalfOpenState: 30 waitDurationInOpenState: 1s failureRateThreshold: 50 eventConsumerBufferSize: 10 ignoreExceptions: - java.lang.IllegalStateException instances: backendA: baseConfig: default backendB: slidingWindowSize: 10 minimumNumberOfCalls: 10 permittedNumberOfCallsInHalfOpenState: 3 waitDurationInOpenState: 1s failureRateThreshold: 50 eventConsumerBufferSize: 10
The integration between resilience4j loaded Circuit breaker registry and spring cloud ReactiveResilience4JCircuitBreakerFactory will be done as the following :
@Bean public ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory(CircuitBreakerRegistry circuitBreakerRegistry) { ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory = new ReactiveResilience4JCircuitBreakerFactory(); reactiveResilience4JCircuitBreakerFactory.configureCircuitBreakerRegistry(circuitBreakerRegistry); return reactiveResilience4JCircuitBreakerFactory; }
How we can test it by using Mock Server Test containers and the proper setup for it :
Now it is time to test that integration and see how it works :
- we will use mock server test containers to mock the target back end service reached out through the gateway
- we will stub some responses
- then trigger the test and monitor the logs to see how the ciruitbreaker react and off course you can get a lot of metrics through Resilience4j supported metrics exposure and events
Add the following dependencies to enable the usage of mock server
<dependency> <groupId>org.testcontainers</groupId> <artifactId>mockserver</artifactId> <version>1.12.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mock-server</groupId> <artifactId>mockserver-client-java</artifactId> <version>3.10.8</version> <scope>test</scope> </dependency>
Now you need to configure the test container starup plus injecting the custom configuration of the route after the startup of the mock server :
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @RunWith(SpringRunner.class) @ContextConfiguration(initializers = {GatewayCircuitBreakerTest.Initializer.class}) public class GatewayCircuitBreakerTest { private static final Logger LOGGER = LoggerFactory.getLogger(GatewayCircuitBreakerTest.class); private static MockServerContainer mockServerContainer; static { mockServerContainer = new MockServerContainer(); mockServerContainer.start(); } static class Initializer implements ApplicationContextInitializer{ public void initialize(ConfigurableApplicationContext configurableApplicationContext) { TestPropertyValues.of( "spring.cloud.gateway.routes[0].id=test-service-withResilient4j", "spring.cloud.gateway.routes[0].uri=" + mockServerContainer.getEndpoint(), "spring.cloud.gateway.routes[0].predicates[0]=" + "Path=/testService/**", "spring.cloud.gateway.routes[0].filters[0]=" + "RewritePath=/testService/(? .*), /$\\{path}", "spring.cloud.gateway.routes[0].filters[1].name=" + "CircuitBreaker", "spring.cloud.gateway.routes[0].filters[1].args.name=" + "backendA", "spring.cloud.gateway.routes[0].filters[1].args.fallbackUri=" + "forward:/fallback/testService" ).applyTo(configurableApplicationContext.getEnvironment()); } } private MockServerClient client = new MockServerClient(mockServerContainer.getContainerIpAddress(), mockServerContainer.getServerPort()); @Autowired private TestRestTemplate template; @AfterClass public static void close(){ mockServerContainer.close(); } }
Finally you can trigger test of the protected endpoint in class GatewayCircuitBreakerTest and see it is going through Resilience4j circuit-breaker by analyzing the reported events in your console :
References:
- Resilienc4j :https://resilience4j.readme.io/docs
- Spring cloud circuit breaker :https://spring.io/projects/spring-cloud-circuitbreaker
- Spring Cloud gateway :https://cloud.spring.io/spring-cloud-gateway/reference/html/
Posted on May 25, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.