Resilience4j is used to protect your microservices to have cascading failure
, it deployed on the consumer
side to protect consumer
if producer
has any failure.
Resilience4j actually has followng 4 parts:
- Circuit Breaks: Cause Resilience4j is deployed at
consumer
side, so we need a way to determine theproducer
side service’s status, and then based on the status (OPEN
,CLOSED
,HALF_OPEN
) to config how we do. The official document is here, which I think it is worth to read - Bulkhead: control how many concurrent threads. It has
Semaphore
(a.k.a object lock) orThreadPool
2 ways to control - RateLimiter : control the interval time of each request to
producer
in nanosecond - Retry: control max time retry number.
Using Resilience4j
You can use decorate
way to use all the function, but I prefer use Annotation
, which is much cleaner
-
Add Dependency
In build.gradle, add following
implementation 'io.github.resilience4j:resilience4j-spring-cloud2:1.1.0'
-
Update the
application.yml
resilience4j.circuitbreaker:
instances:
producer-helloworld-service: #this is the name/label to be used later
registerHealthIndicator: true
ringBufferSizeInClosedState: 5
ringBufferSizeInHalfOpenState: 3
waitDurationInOpenState: 10s
failureRateThreshold: 50
recordExceptions:
- org.springframework.web.client.HttpServerErrorException
- java.io.IOException
- java.util.concurrent.TimeoutException
- org.springframework.web.client.ResourceAccessException
- org.springframework.web.client.HttpClientErrorException
ignoreExceptions:
resilience4j.ratelimiter:
instances:
producer-helloworld-service: #this is the name/label to be used later
limitForPeriod: 5
limitRefreshPeriod: 10000 #ms
timeoutDuration: 1000ms
resilience4j.retry:
instances:
producer-helloworld-service: #this is the name/label to be used later
maxRetryAttempts: 3
waitDuration: 5000
resilience4j.bulkhead:
instances:
producer-helloworld-service: #this is the name/label to be used later
max-concurrent-calls: 10
maxWaitDuration: 10ms
as you can see we config the 4 parts.
-
Add
fallbackXXX
methods inconsumer
-
Add annotation to the
consumer
@GetMapping("/withRestTemplate") @CircuitBreaker(name="producer-helloworld-service", fallbackMethod = "fallback") @Bulkhead(name = "producer-helloworld-service", type = Bulkhead.Type.SEMAPHORE, fallbackMethod = "fallbackBulkhead") @RateLimiter(name = "producer-helloworld-service", fallbackMethod = "fallbackForRatelimit") @Retry(name = "get", fallbackMethod = "fallbackRetry") public String testHelloWorldService(){ String answer = this.restTemplate.getForObject("http://producer-helloworld-service/test/greeting", String.class); return getAnswer(answer); }
as you can see the name
attribution in each annotation match the application.yml
, and you need to create fallback
methods to handle different situation.