Conteúdo do item:
Como evitar que uma falha de rede ou serviço se espalhe para outros serviços?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-reactor</artifactId>
<version>1.7.1</version>
</dependency>
Application.properties
#Criando para ser alterado no CircuitConfig
resilience4j.circuitbreaker.instances.externalServiceFoo.slidingWindowType=COUNT_BASED
package br.com.byiorio.performance_test.infra;
import java.time.Duration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType;
import io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigCustomizer;
@Configuration
public class CircuitConfig {
// https://resilience4j.readme.io/docs/circuitbreaker
@Bean
public CircuitBreakerConfigCustomizer externalServiceFooCircuitBreakerConfig() {
return CircuitBreakerConfigCustomizer
.of("externalServiceFoo",
builder -> builder.slidingWindowSize(10)
.slidingWindowType(SlidingWindowType.COUNT_BASED)
.waitDurationInOpenState(Duration.ofSeconds(5))
.minimumNumberOfCalls(5)
.failureRateThreshold(50.0f));
}
}
Controller Advice:
package br.com.byiorio.performance_test.infra;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.reactive.function.client.WebClientRequestException;
@ControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("errorCode", "122");
body.put("errorMessage", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.SERVICE_UNAVAILABLE);
}
@ExceptionHandler(WebClientRequestException.class)
public ResponseEntity<Object> handleException(WebClientRequestException ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("errorCode", "122");
body.put("errorMessage", ex.getMessage());
return new ResponseEntity<>(body, HttpStatus.SERVICE_UNAVAILABLE);
}
}
Anotação:
@CircuitBreaker(name = "externalServiceFoo")
public Mono<Object> usandoWebClientSemBlockCircuit(Integer accountNumber) {
// Primeira Chamada
Mono<CardResponse> card = httpClientLocalhost
.get()
.uri(HTTP_LOCALHOST_9090.concat(accountNumber.toString()).concat("/card"))
.retrieve()
.bodyToMono(CardResponse.class);
// Segunda Chamada
Mono<StatusResponse> status = httpClientLocalhost
.get()
.uri(HTTP_LOCALHOST_9091.concat(accountNumber.toString()).concat("/status"))
.retrieve()
.bodyToMono(StatusResponse.class);
// Execução em paralelo
return Mono.zip(card, status)
.map(respostas -> {
return BalanceResponse.builder()
.status(respostas.getT2().getCode())
.cardNumber(respostas.getT1().getCardNumber())
.balance(this.getBalance(accountNumber))
.build();
});
}
Circuit Breaker