13 Eylül 2021 Pazartesi

Resilience4j @CircuitBreaker Anotasyonu

Giriş
Şu satırı dahil ederiz
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
Şu 
Hystrix yerine artık bunu kullanmak daha iyi olabilir.

Maven
Şu satırı dahil ederiz. @CircuitBreaker anotasyonu kullanılır
<dependency>
  <groupId>io.github.resilience4j</groupId>
  <artifactId>resilience4j-spring-boot2</artifactId>
  <version>1.7.1</version>
</dependency>
Eğer SpringCloud kullanıyorsak şu satırı dahil ederiz
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
Resilience4j  vs Hystrix
Şeklen şöyle

CircuitBreaker Durumu
3 durumda olabilir. Açıklaması şöyle
A circuit breaker consists of three states :

Closed — This state indicates that everything is working fine.

Open — This state indicates that a failure has happened. By changing the state from closed to open, it returns a specific response directly without processing the business logic.

With the occurrence of failure, the state of circuit breaker will turn into open state and avoids processing the logic. So there must be a mechanism to rollback state from open to closed state when everything goes back to normal. There is now the third state present here.

Half-open — This state indicates that the circuit breaker is ready to recall the unresponsive service as trial. If the problem with the service is fixed, it will turn the state from open to closed.
CircuitBreaker Çeşitleri
Açıklaması şöyle
1. Time based circuit breaker
The time-based circuit breaker turns into open state, if 80% of requests out of 10 requests fail.

2. Count based circuit breaker
The count-based circuit breaker turns into open state, if 80% of requests out of 10 requests takes more than 2 second to respond.
@CircuitBreaker Anotasyonu Alternatifi
Şöyle yaparız
import io.github.resilience4j.circuitbreaker.CircuitBreaker;

public class ResilienceServiceImpl {

  private String url;

    private final RestTemplate restTemplate;
    private final CircuitBreaker circuitBreaker;

  @Override
  public Response process(Request request , Integer wait) 
  throws Exception {

    Supplier<Response> supplier = circuitBreaker
      .decorateSupplier(() -> callServer(request , wait));

  }

  @Override
  public Response callServer(Request request , Integer wait) {
    return restTemplate.exchange(...).getBody();
  }
}
application.properties yerine CircuitBreakerConfig kullanılabilir. Şöyle yaparız
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;

@Bean
public CircuitBreaker circuitBreaker() {
  CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig
    .custom()
    // time based
    .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
    .minimumNumberOfCalls(3)
    .slidingWindowSize(10)
    .failureRateThreshold(70.0f)
    // count based
    .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)
    .slidingWindowSize(10)
    .slowCallRateThreshold(80.0f)
    .slowCallDurationThreshold(Duration.ofSeconds(2))
    // hal-open state configuration
    .waitDurationInOpenState(Duration.ofSeconds(10))
    .permittedNumberOfCallsInHalfOpenState(2)
    .build();

    CircuitBreakerRegistry circuitBreakerRegistry = 
      CircuitBreakerRegistry.of(circuitBreakerConfig);

    return circuitBreakerRegistry.circuitBreaker("server");
}
name Alanı
Açıklaması şöyle
This means that Resilience4j will automatically create a circuit breaker for this method with the name "default". If this method starts failing (i.e. throwing exceptions) at a certain rate, the circuit breaker will trip and start returning a fallback response instead of executing the method. This can help prevent cascading failures and improve the resilience of the overall system.

By default, the circuit breaker will trip if more than 50% of requests to the annotated method fail. You can configure this and other properties of the circuit breaker using the @CircuitBreaker annotation's other attributes, such as failureRateThreshold, waitDurationInOpenState, and ringBufferSizeInClosedState.
Şöyle yaparız
@GetMapping("/ms1")
@CircuitBreaker(name="default" fallbackMethod = "handleFallback")
public ResponseEntity<String> callingMS2() {
  String response = restTemplate.getForObject("http://...",String.class);
  return new ResponseEntity<String> (response,HttpStatus.OK);
}

ResponseEntity<String> handleFallback)(Exception e) {
  return new ResponseEntity<String>("Handled fallback",HttpStatus.INTERNAL_SERVER_ERROR);
}
fallbackMethod  Alanı
Örnek
Şöyle yaparız
@GetMapping
@CircuitBreaker(name = CALLER_SERVICE, fallbackMethod = "serviceFallback")
public String service() {
  String url = BASE_URL + "supplier";
  return restTemplate.getForObject(
    url,
    String.class
  );
}

public String serviceFallback(Exception e) {
   return "Response returned from fallback method of caller service";
}
Örnek
Şöyle yaparız. Burada iki tane fallback metodu tanımlı. Farkları metod imzasındaki exception tipi
@Slf4j
@Component
public class AccountLookupRouter {
  @Autowired
  private BankOneAccountLookupService bankOneService;
  @Autowired
  private BankTwoAccountLookupService bankTwoService;
  
  // Primary lookup with circuit breaker.
  @CircuitBreaker(name = "lookupAccount", fallbackMethod = "lookupAccountFallback")
  public AccountLookupResponse lookupAccount(String iban, String country, 
    String currency) {
    return bankOneService.lookupAccount(iban, country, currency);
  }
  
  // Fallback method for 4xx exceptions, just percolate the exception back.
  private AccountLookupResponse lookupAccountFallback(String iban, String country, 
    String currency, HttpClientErrorException e) {
    return bankOneService.lookupAccount(iban, country, currency);
  }
  
  // Fallback method for all other exception types.
  private AccountLookupResponse lookupAccountFallback(String iban, String country, 
    String currency, Throwable t) {
    return bankTwoService.lookupAccount(iban, country, currency);
  }
}
Örnek - reactor
Şu satırı dahil ederiz
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
  </dependency>

  <dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
  </dependency>
  <dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-reactor</artifactId>
  </dependency>
</dependencies> 
application.properties şöyledir
# Resiliece4j Configuration
resilience4j.circuitbreaker.configs.shared.register-health-indicator=true
resilience4j.circuitbreaker.configs.shared.sliding-window-type=count_based
resilience4j.circuitbreaker.configs.shared.sliding-window-size=5
resilience4j.circuitbreaker.configs.shared.failure-rate-threshold=40
resilience4j.circuitbreaker.configs.shared.slow-call-rate-threshold=40
resilience4j.circuitbreaker.configs.shared.permitted-number-of-calls-in-half-open-state=1
resilience4j.circuitbreaker.configs.shared.max-wait-duration-in-half-open-state=10s
resilience4j.circuitbreaker.configs.shared.wait-duration-in-open-state=10s
resilience4j.circuitbreaker.configs.shared.slow-call-duration-threshold=2s
resilience4j.circuitbreaker.configs.shared.writable-stack-trace-enabled=true
resilience4j.circuitbreaker.configs.shared
.automatic-transition-from-open-to-half-open-enabled=true

resilience4j.circuitbreaker.instances.example.base-config=shared
Açıklaması şöyle
Sliding-window-type: we will use a count-based circuit breaker type. In this type, the circuit will trip or move to an open state based on each incoming request.

Sliding-window-size: we will use this parameter to record the last N requests to make the circuit breaker trip or open. Here, we will record the last 5 requests.

Failure-rate-threshold: it shows the percentage of the total sliding-window-size that fails and will cause the circuit breaker trips to open state. This means, with a configuration of 40%, 2 out of 5 failed requests will cause the circuit breaker trips to open state.

Slow-call-rate-threshold: it shows the percentage of the total sliding-window-size that fails which will cause the circuit breaker trips to open state. From the configuration above, it can be seen that 2 out of 5 failed requests will cause the circuit breaker trips to open state.

Slow-call-duration-threshold: is the time taken to indicate the received response exceeds this configuration time will be recorded as an error count.
Şöyle yaparız
private static final String RESILIENCE4J_INSTANCE_NAME = "example";

@CircuitBreaker(name = RESILIENCE4J_INSTANCE_NAME)
public Mono<Response<Boolean>> delay(@PathVariable int delay) {
  return Mono.just(toOkResponse())
      .delayElement(Duration.ofSeconds(delay));
}
Şöyle yaparız
private static final String RESILIENCE4J_INSTANCE_NAME = "example";
private static final String FALLBACK_METHOD = "fallback";

@CircuitBreaker(name = RESILIENCE4J_INSTANCE_NAME, fallbackMethod = FALLBACK_METHOD)
public Mono<Response<Boolean>> delay(@PathVariable int delay) {
  return Mono.just(toOkResponse())
      .delayElement(Duration.ofSeconds(delay));
}

public Mono<Response<Boolean>> fallback(Exception ex) {
    return Mono.just(toResponse(HttpStatus.INTERNAL_SERVER_ERROR, Boolean.FALSE))
        .doOnNext(result -> log.warn("fallback executed"));
}

Hiç yorum yok:

Yorum Gönder