서비스 하나를 (fallback 날릴) 만들어서 gateway instance로 추가해준다.
application.yml
spring:
application:
name: scg-gateway
cloud:
gateway:
routes:
- id: test-svc
uri: lb://TEST-SVC
predicates:
- Path=/testSvc/**
filters:
- name: CircuitBreaker
args:
name: testCircuitBreaker
fallbackUri: forward:/fallback
resilience4j.circuitbreaker:
configs:
default:
failureRateThreshold: 50
slowCallRateThreshold: 100
slowCallDurationThreshold: 60000
permittedNumberOfCallsInHalfOpenState: 4
maxWaitDurationInHalfOpenState: 1000
slidingWindowType: COUNT_BASED
slidingWindowSize: 10
minimumNumberOfCalls: 10
waitDurationInOpenState: 10000
registerHealthIndicator: true
recordExceptions: java.util.concurrent.TimeoutException
eventConsumerBufferSize: 10
instances:
test-svc:
baseConfig: default
resilience4j.timelimiter:
configs:
default:
timeoutDuration: 3s
management:
health:
circuitbreakers:
enabled: true
endpoint:
health:
showDetails: always
gateway:
enabled: true
endpoints:
web:
exposure:
include: '*'
metrics:
enable:
resilience4j:
circuitbreaker:
calls: true
gateway 라우트 설정 중 filters에 CircuitBreaker를 등록했다.
args Name은 작성한 대로 /actuator/health 에서 확인할 수 있다.
fallbackUri에 fallback시 호출할 Uri를 작성해준다.
주의할 점은 forward://이 아니라 forward:/ <-- 하나다.
recordExceptions : 서킷을 잡을 에러를 등록했다. 제일 만만하게 테스트할 수 있는게 타임아웃이다.
resilience4j.timelimiter.configs.default.timeoutDuration: 3s
3초 이후에 오면 fallback을 호출하도록 설정했다.
Test서비스의 TestController.java
@GetMapping("/test")
public String test(@RequestParam(required = false)String id) throws InterruptedException {
int randomNo = new Random().nextInt();
String result = randomNo % 2 == 0? testService.test(): testService.fail();
return result;
}
랜덤하게 성공과 실패를 호출하도록 한다.
TestService.java
@Service
public class TestService {
public String test() {
return "hi";
}
public String fail() throws InterruptedException {
Thread.sleep(5000);
return "fail";
}
}
fail이 호출될 경우 5초 sleep을 준다.
게이트웨이 time duration이 3초 이므로 TimeoutException이 발생한다.
Gateway FallbackController.java
@RestController
public class FallbackController {
@GetMapping("/fallback")
public Mono<Void> fallback(ServerWebExchange exchange) {
Throwable t = exchange.getAttribute(ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR);
if(t instanceof TimeoutException) {
throw new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, "Service Timeout");
} else if (t instanceof CallNotPermittedException) {
throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "Service is not available");
}
return null;
}
}
Exception을 잡아서 따로 처리할 수도 있다.
CallNotPermittedException은 서킷이 오픈되었을 때 발생하는 Exception이다.
서버를 올리고 처음 호출했을 때 서킷의 상태
health체크하는 방법은 아래링크
https://tweety1121.tistory.com/entry/Spring-circuitbreaker-actuator-health-check-%EC%84%A4%EC%A0%95
계속 호출하면서 서킷이 오픈되면
서킷의 상태가 바뀐다.
fallback컨트롤러에서 정의한 상태대로 리턴된다.
{
"timestamp": "2022-02-04T12:50:55.614+00:00",
"path": "/testSvc/test",
"status": 503,
"error": "Service Unavailable",
"message": "Service is not available"
}
'Spring' 카테고리의 다른 글
Spring Boot2 Swagger 사용 (0) | 2022.02.08 |
---|---|
Spring restTemplate Connection pool 사용 (0) | 2022.02.07 |
Spring cloud circuit breaker fallback 메소드 테스트 (0) | 2022.02.04 |
Spring circuitbreaker actuator health check 설정 (0) | 2022.02.04 |
Spring Cloud Gateway Global Error Handler (0) | 2022.02.03 |