fallbackMethod
를 실행하는 것을 확인합니다.서킷 브레이커가 localhost:19090
에서 실행되는 흐름은 다음과 같습니다:
사용자가 브라우저나 API 클라이언트를 통해 http://localhost:19090/product/111
URL로 요청을 보냅니다.
ProductController
가 요청을 처리Spring Boot 애플리케이션이 요청을 수신하고, 이 요청은 ProductController
의 getProduct
메서드로 전달됩니다.
@RestController
@RequiredArgsConstructor
public class ProductController {
private final ProductService productService;
@GetMapping("/product/{id}")
public Product getProduct(@PathVariable("id") String id) {
return productService.getProductDetails(id);
}
}
ProductService
에서 비즈니스 로직 실행ProductController
는 ProductService
의 getProductDetails
메서드를 호출하여 해당 제품의 세부 정보를 가져오려고 합니다. 이 메서드는 @CircuitBreaker
애노테이션이 적용되어 있습니다.
@Service
@RequiredArgsConstructor
public class ProductService {
private final Logger log = LoggerFactory.getLogger(getClass());
private final CircuitBreakerRegistry circuitBreakerRegistry;
@PostConstruct
public void registerEventListener() {
circuitBreakerRegistry.circuitBreaker("productService").getEventPublisher()
.onStateTransition(event -> log.info("#######CircuitBreaker State Transition: {}", event))
.onFailureRateExceeded(event -> log.info("#######CircuitBreaker Failure Rate Exceeded: {}", event))
.onCallNotPermitted(event -> log.info("#######CircuitBreaker Call Not Permitted: {}", event))
.onError(event -> log.info("#######CircuitBreaker Error: {}", event));
}
@CircuitBreaker(name = "productService", fallbackMethod = "fallbackGetProductDetails")
public Product getProductDetails(String productId) {
log.info("###Fetching product details for productId: {}", productId);
if ("111".equals(productId)) {
log.warn("###Received empty body for productId: {}", productId);
throw new RuntimeException("Empty response body");
}
return new Product(productId, "Sample Product: " + productId);
}
public Product fallbackGetProductDetails(String productId, Throwable t) {
log.error("####Fallback triggered for productId: {} due to: {}", productId, t.getMessage());
try {
log.info("####Fallback method started sleeping for 5 seconds");
Thread.sleep(5000);
log.info("####Fallback method finished sleeping");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("####Fallback method interrupted", e);
}
return new Product(productId, "Fallback Product");
}
}
productId
가 111
이 아닌 경우 정상적으로 제품 정보를 반환합니다.productId
가 111
인 경우 RuntimeException
을 발생시켜 오류를 기록합니다.Fallback
메서드 호출getProductDetails
메서드에서 예외가 발생하면 fallbackGetProductDetails
메서드가 호출됩니다. 이 메서드는 5초 동안 지연 시간을 둔 후 폴백 응답을 반환합니다.
public Product fallbackGetProductDetails(String productId, Throwable t) {
log.error("####Fallback triggered for productId: {} due to: {}", productId, t.getMessage());
try {
log.info("####Fallback method started sleeping for 5 seconds");
Thread.sleep(5000);
log.info("####Fallback method finished sleeping");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("####Fallback method interrupted", e);
}
return new Product(productId, "Fallback Product");
}
application.yml
파일에서 서킷 브레이커 설정이 정의되어 있습니다.
spring:
application:
name: sample
server:
port: 19090
resilience4j:
circuitbreaker:
configs:
default:
registerHealthIndicator: true
slidingWindowType: COUNT_BASED
slidingWindowSize: 5
minimumNumberOfCalls: 5
slowCallRateThreshold: 100
slowCallDurationThreshold: 60000
failureRateThreshold: 50
permittedNumberOfCallsInHalfOpenState: 3
waitDurationInOpenState: 5s # 5초로 설정
management:
endpoints:
web:
exposure:
include: prometheus
prometheus:
metrics:
export:
enabled: true
http://localhost:19090/product/111
로 요청을 보냄.ProductController
가 요청을 받아 ProductService
의 getProductDetails
메서드를 호출.productId
가 111
인 경우 예외 발생.Fallback
메서드 호출.Fallback
메서드에서 5초 지연 후 폴백 응답 반환.