Spring Cloud에서 서킷 브레이커를 활용한 간단한 fallback 예제입니다.
이번에도 small make 방식으로 구현했습니다.
api1번에서 api2번을 호출 할 때 2번이 내려간 경우 클라이언트에게 500에러가 아닌 미리 정의된 fallback을 실행하도록 합니다.
동영상 🎬
소스 💻
api 1번
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.10'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.test.lsy'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
// Resilience4j for Spring Boot 3 (CircuitBreaker 등)
implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.1.0'
// AOP 필수 (@CircuitBreaker 동작 위해)
implementation 'org.springframework.boot:spring-boot-starter-aop'
// (선택) Actuator: /actuator/health 에 Circuit 상태 표시 가능
implementation 'org.springframework.boot:spring-boot-starter-actuator'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
application.yml
server:
port: 8081
resilience4j:
circuitbreaker:
circuit-breaker-aspect-order: 1
configs:
default:
slidingWindowType: COUNT_BASED
slidingWindowSize: 5
failureRateThreshold: 50
minimumNumberOfCalls: 5
waitDurationInOpenState: 5s
slowCallDurationThreshold: 3s
slowCallRateThreshold: 60
permittedNumberOfCallsInHalfOpenState: 5
automaticTransitionFromOpenToHalfOpenEnabled: true
registerHealthIndicator: true
instances:
api2Circuit:
baseConfig: default
management:
health:
circuitbreakers:
enabled: true
endpoints:
web:
exposure:
include: health
endpoint:
health:
show-details: always
RestTemplateConfig
package com.test.lsy.api1.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
FallbackService
package com.test.lsy.api1.service;
import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class FallbackService {
public String fallbackApi2(Throwable t) {
if(t instanceof CallNotPermittedException) {
log.warn("⚠️ CircuitBreaker OPEN 상태: 호출 차단됨. fallback 진입.");
} else {
log.warn("❌ API 호출 중 예외 발생: {}, fallback 진입.", t.toString());
}
return "API2 서버가 응답하지 않아 fallback 처리됨";
}
}
ApiController
package com.test.lsy.api1.controller;
import com.test.lsy.api1.service.FallbackService;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequiredArgsConstructor
@Slf4j
public class ApiController {
private final RestTemplate restTemplate;
private final FallbackService fallbackService;
private static final String REQUEST_URL = "http://localhost:8082/hello";
@GetMapping("/call-api2")
@CircuitBreaker(name = "api2Circuit", fallbackMethod = "fallbackApi2")
public String callApi2() {
return restTemplate.getForObject(REQUEST_URL, String.class);
}
public String fallbackApi2(Throwable t) {
return fallbackService.fallbackApi2(t);
}
}
api 2번
ApiController
package com.test.lsy.api2.controller;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequiredArgsConstructor
public class ApiController {
@GetMapping("/hello")
public String hello() {
return "API2 정상 응답";
}
}
개인 스터디 기록을 메모하는 공간이라 틀린점이 있을 수 있습니다.
틀린 점 있을 경우 댓글 부탁드립니다.
'IT > Live Coding' 카테고리의 다른 글
[⏱️ 21분 만에 구축] 🚀 Spring Gateway + 🔄 Redis + 🐘 PostGreSQL 연동 (⚡간단한 캐시 처리) (1) | 2025.04.26 |
---|---|
RoboPOJOGenerator로 외부 API JSON 쉽게 자바 객체로 변환하기 (테스트 영상 & 소스코드 포함) (0) | 2025.04.07 |
Spring Cloud Gateway + API 서버 연동 예제 🚀 (테스트 영상 & 소스코드 포함) (0) | 2025.04.01 |
Spring Boot + Jasypt를 이용한 암호화 테스트 🔐 (테스트 영상 & 소스코드 포함) (0) | 2025.03.22 |
Spring Boot + JWT로 인증 시스템 구현 (테스트 영상 & 소스 코드 포함) (0) | 2025.03.22 |
댓글