1. CircuitBreaker๊ฐ ํ์ํ ์ด์
๊ฐ๋ฐ์ ํ๋ค ๋ณด๋ฉด ์ธ๋ถ API๋ฅผ ํธ์ถํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค. ํนํ๋ ์ ์ฒด์ ์ธ ์์คํ ๊ตฌ์ฑ์ด MSA(Microservice Architecture)๋ก ๋์ด ์๋ค๋ฉด ๋ค๋ฅธ ์๋น์ค๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ๊ฐ ๋งค์ฐ ๋น๋ฒํ๋ค.
๋ฌธ์ ๋ ์๋ฒ๋ค์ ์ฅ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค๋ ์ ์ธ๋ฐ, ํธ์ถํ ๋ค๋ฅธ ์๋น์ค์ ์ฅ์ ๊ฐ ๋ฐ์ํ๋ค๋ฉด ์ฅ์ ๊ฐ ์ ํ๋์ด, ํด๋น ์๋น์ค๊น์ง ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. ๋ํ ์ฅ์ ๊ฐ ๋ฐ์ํ ์๋ฒ์ ๊ณ์ ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ์ ์ฅ์ ๋ณต๊ตฌ๋ฅผ ํ๋ค๊ฒ ๋ง๋ ๋ค.
์๋ฅผ ๋ค์ด, ์ฌ์ฉ์์ ๋์๋ณด๋์์ ํ ๋ฒ์ ์์ฒญ์ผ๋ก ์ฌ์ฉ์์ ์ฌ๋ฌ ์ ๋ณด๋ฅผ ์กฐํํด ํ ํ๋ฉด์ ๋ณด์ฌ์ฃผ์ด์ผ ํ๋ ์ํฉ์ ์๊ฐํด๋ณด์. ์ด ๋์๋ณด๋๋ ์๋์ ๊ฐ์ด ๊ฐ๊ธฐ ๋ค๋ฅธ ์๋น์ค์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค.
- ์ฌ์ฉ์ ์ ๋ณด ์๋น์ค: ์ฌ์ฉ์ ํ๋กํ ์ ๋ณด๋ฅผ ๋ฐํ
- ์ฃผ๋ฌธ ๋ด์ญ ์๋น์ค: ์ฌ์ฉ์์ ์ต๊ทผ ์ฃผ๋ฌธ ๋ด์ญ์ ๋ฐํ
- ์ถ์ฒ ์๋น์ค: ์ฌ์ฉ์์ ๊ด์ฌ์ฌ์ ๋ง๋ ์ถ์ฒ ์ํ ๋ชฉ๋ก์ ๋ฐํ
๋ง์ฝ ์ด ์ค ํ๋์ ์๋น์ค, ์๋ฅผ ๋ค์ด ์ถ์ฒ ์๋น์ค์ ์ฅ์ ๊ฐ ๋ฐ์ํ๋ค๋ฉด, ๋์๋ณด๋ ์๋น์ค๋ ์ถ์ฒ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ฉด์ ์๋ต์ด ์ง์ฐ๋๋ค. ์ด๋ ๋์๋ณด๋ ์ ์ฒด์ ์๋ต ์๋๋ฅผ ๋ฆ์ถ๊ฒ ๋ ๋ฟ๋ง ์๋๋ผ, ์ต์ ์ ๊ฒฝ์ฐ ์ฅ์ ๊ฐ ๋ ์๋น์ค๋ก ์ธํด ์ ์ฒด ๋์๋ณด๋๊ฐ ์๋ตํ์ง ์๋ ์ํฉ์ด ๋ฐ์ํ ์๋ ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ์ฅ์ ๊ฐ ๋ฐ์ํ ์๋น์ค๋ฅผ ํ์งํ๊ณ , ์์ฒญ์ ๋ณด๋ด์ง ์๋๋ก ์ฐจ๋จํ ํ์๊ฐ ์๋ค. ์ด๋ ์ฌ์ฉ๋๋ ๊ฒ์ด ์ํท ๋ธ๋ ์ด์ปค์ด๋ค.
2. CircuitBreaker๋?
์๋ ์ํท ๋ธ๋ ์ด์ปค๋ ํด์ ๊ทธ๋๋ก ๋์ ์ฐจ๋จ๊ธฐ ๋ผ๋ ๋ป์ ์ง๋๋ค. ๋์ ์ฐจ๋จ๊ธฐ๋ ์ ๊ธฐ ํ๋ก์์ ๊ณผ๋ถํ๊ฐ ๊ฑธ๋ฆฌ๊ฑฐ๋ ๋จ๋ฝ์ผ๋ก ์ธํ ํผํด๋ฅผ ๋ง๊ธฐ ์ํด ์๋์ผ๋ก ํ๋ก๋ฅผ ์ ์ง์ํค๋ ์ฅ์น์ด๋ค.
์๋ฒ์์ ์ฌ์ฉํ๋ ์ํท ๋ธ๋ ์ด์ปค๋ ์ธ๋ถ API ํต์ ์ ์ฅ์ ์ ํ๋ฅผ ๋ง๊ธฐ ์ํด ์ฅ์ ๋ฅผ ํ์งํ๋ฉด ์ธ๋ถ์์ ํต์ ์ ์ฐจ๋จํ๋ ์ญํ ์ ํ๋ค.
์ํท ๋ธ๋ ์ด์ปค๊ฐ ์คํ(์คํ)๋๋ฉด fail-fast ํจ์ผ๋ก์จ ์ธ๋ถ ์๋น์ค๊ฐ ์ฅ์ ๊ฐ ๋๋๋ผ๋ ๋น ๋ฅด๊ฒ ์๋ฌ๋ฅผ ์๋ต ๋ฐ์ ์ ์๋ ์ฅ์ ์ด ์์ผ๋ฉฐ, ๊ฐ๋ฐ์๊ฐ ์ง์ ํ ํ์๋ฅผ ๋ฆฌํด ๋ฐ์ ์๋ ์๋ค.
3. CircuitBreaker ๋์ ์๋ฆฌ
์ํท ๋ธ๋ ์ด์ปค์ 3๊ฐ์ง ์ํ
์ค์ ํ๋ก๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๊ตฌ๊ฐ ์ธ๋ถ API ๋๋ Callee์ ํด๋นํ๊ณ , Power Source๊ฐ ํด๋ผ์ด์ธํธ(ํธ์ถ ์๋ฒ) ๋๋ Caller์ ํด๋นํ๋ค. ๊ทธ๋ฆฌ๊ณ ํ๋ก ์ฐจ๋จ๊ธฐ์๋ ํฌ๊ฒ CLOSED, OPEN, HALF_OPEN 3๊ฐ์ง ์ํ๊ฐ ์กด์ฌํ๋๋ฐ, ๊ฐ๊ฐ์ ์ํ๋ฅผ ์ ๋ฆฌํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
- CLOSED: circuit์ด ๋ซํ ์ํ๋ก, ์ธ๋ถ ํธ์ถ์ด ์ ์์ํ์ด๋ค.
- OPEN: circuit์ด ์ด๋ฆฐ ์ํ๋ก, ์ค๋ฅ(์คํจํธ์ถ/๋๋ฆฐํธ์ถ)๊ฐ ๋ฐ์ํ์ ๋ OPEN ์ํ๋ก ์ ํ๋๋ค.
- HALF_OPEN: OPEN ์ํ๊ฐ ๋ฐ์ํ ์ดํ ์ผ์ ์๊ฐ์ด ์ง๋ ์ํ์ด๋ค. ์ด๋ ์ํ๊ฐ ์ ์์ ์ด๋ผ๋ฉด CLOSED๋ก, ์๋๋ผ๋ฉด ๋ค์ OPEN์ผ๋ก ์ ํ๋๋ค.
์ํท ๋ธ๋ ์ด์ปค์ ์ฌ๋ผ์ด๋ฉ ์๋์ฐ
์ฌ๋ผ์ด๋ฉ ์๋์ฐ๋ ์๊ฐ ๊ธฐ๋ฐ(Time-Based)๊ณผ ๊ฐ์ ๊ธฐ๋ฐ(Count-Based)์ 2๊ฐ์ง ํ์ ์ ๊ฐ๋๋ค.
- Time-Based: ํน์ ๊ธฐ๊ฐ(Duration) ์ ํธ์ถ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ ์ฌ๋ผ์ด๋ฉ ์๋์ฐ๋ฅผ ์ ๊ณต
- Count-Based: ํน์ ๊ฐ์(Count) ์ ํธ์ถ ๊ฒฐ๊ณผ๋ฅผ ์ ์ฅํ๋ ์ฌ๋ผ์ด๋ฉ ์๋์ฐ๋ฅผ ์ ๊ณต
์ํท ๋ธ๋ ์ด์ปค์ ๋์ ํ๋ก์ธ์ค๋ ์๋์ ๊ฐ๋ค.
1. ์ผ๋ฐ์ ์ผ๋ก ์ธ๋ถ ์๋ฒ๋ ์ ์์ ์ผ๋ก ์คํ ์ค์ด๋ฉฐ, ์ํท ๋ธ๋ ์ด์ปค๋ ๋ซํ ์์ด ๋ชจ๋ ์์ฒญ์ด ์ ๋ฌ๋๊ณ ์๋ต์ ๋ฐ๋๋ค.
2. ์ธ๋ถ ์๋ฒ์ ์ฅ์ ๊ฐ ๋ฐ์ํ์ฌ ์์ฒญ์ด ์คํจํ๊ธฐ ์์
3. ์์ฒญ ์คํจ๊ฐ ๊ธฐ์ค์น๋ฅผ ๋์ด๊ฐ๋ฉด ์ํท ๋ธ๋ ์ด์ปค๊ฐ ์ด๋ฆฌ๊ณ ๋ชจ๋ ์์ฒญ์ด ์ฆ์ ์คํจํ๋ค.
4. ์ํท ๋ธ๋ ์ด์ปค๊ฐ ์ด๋ฆฐ ์ํ์์ ๋ชจ๋ ์์ฒญ์ด ์ธ๋ถ ์๋ฒ๋ก ์ ๋ฌ๋์ง ์๊ณ ์ฆ์ ์๋ฌor์๋ต์ ๋ฐํํ๋ค.
5. ์ธ๋ถ ์๋ฒ๊ฐ ์ ์์ ์ผ๋ก ๋ณต๊ตฌ๋๋ค.
6. ์ํท ๋ธ๋ ์ด์ปค๊ฐ OPEN ์ํ๋ก ์ ํ๋ ํ ์ค์ ํ ์๊ฐ์ด ์ง๋๋ฉด HALF_OPEN ์ํ๋ก ๋ณ๊ฒฝ๋๋ค. ์ด ์ํ์์ ์ผ๋ถ ์์ฒญ๋ง ์ธ๋ถ ์๋ฒ๋ก ์ ๋ฌํ์ฌ ์๋ฒ๊ฐ ์ ์์ ์ผ๋ก ๋์ํ๋์ง ํ ์คํธํ๋ค.
7. HALF_OPEN ์ํ์์ ์์ฒญ์ด ์ฑ๊ณตํ๋ฉด, ์ํท ๋ธ๋ ์ด์ปค๋ ๋ค์ ๋ซํ ๋ชจ๋ ์์ฒญ์ด ์ ์์ ์ผ๋ก ์ ๋ฌ๋๋ค. ๋ง์ฝ ์คํจํ๋ฉด ๋ค์ OPEN ์ํ๋ก ์ ํ๋๋ค.
4. Resilience4J ๋์
์์กด์ฑ ์ถ๊ฐ
// resilience4j
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j'
Resilience4J ์ค์
@Configuration
@RequiredArgsConstructor
@Slf4j
public class Resilience4jConfig {
private final RetryRegistry retryRegistry;
@Bean
public CircuitBreaker circuitBreaker(CircuitBreakerRegistry circuitBreakerRegistry) {
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(
"customCircuitBreaker",
CircuitBreakerConfig.custom()
.failureRateThreshold(50) // ์คํจ์จ ๊ธฐ์ค
.slowCallRateThreshold(50) // ๋๋ฆฐ ํธ์ถ ์คํจ์จ
.slowCallDurationThreshold(Duration.ofSeconds(3)) // ๋๋ฆฐ ํธ์ถ ํ๋จ ๊ธฐ์ค ์๊ฐ
.permittedNumberOfCallsInHalfOpenState(3) // Half-Open ์ํ์์ ํ์ฉ๋๋ ํธ์ถ ํ์
.maxWaitDurationInHalfOpenState(Duration.ofSeconds(3)) // Half-Open ์ํ์์ ์ต๋ ๋๊ธฐ ์๊ฐ
.slidingWindowType(COUNT_BASED) // ํธ์ถ ํ์ ๊ธฐ์ค์ผ๋ก ํ๋จ
.slidingWindowSize(10) // ์ต๊ทผ 10๋ฒ ํธ์ถ ๊ธฐ์ค์ผ๋ก ์คํจ์จ ๊ณ์ฐ
.minimumNumberOfCalls(5) // ์ต์ ํธ์ถ ํ์
.waitDurationInOpenState(Duration.ofSeconds(60)) // Open ์ํ์์ ๋๊ธฐ ์๊ฐ
.build()
);
return circuitBreaker;
}
@Bean
public Retry retry() {
return retryRegistry.retry("customRetry",
RetryConfig.custom()
.maxAttempts(3) // ์ต๋ ์ฌ์๋ ํ์
.waitDuration(Duration.ofMillis(500)) // ์ฌ์๋ ๊ฐ ๋๊ธฐ ์๊ฐ
.build()
);
}
}
Config๋ก ์ค์ ํด๋ ๋์ง๋ง yml ํ์ผ์์ ์ค์ ํด๋ ๋๋ค.
resilience4j:
circuitbreaker:
instances:
customCircuitBreaker: # Circuit Breaker ์ด๋ฆ
failureRateThreshold: 50 # ์คํจ์จ ๊ธฐ์ค
slowCallRateThreshold: 50 # ๋๋ฆฐ ํธ์ถ ์คํจ์จ
slowCallDurationThreshold: 3s # ๋๋ฆฐ ํธ์ถ ํ๋จ ๊ธฐ์ค ์๊ฐ
permittedNumberOfCallsInHalfOpenState: 3 # Half-Open ์ํ์์ ํ์ฉ๋๋ ํธ์ถ ํ์
maxWaitDurationInHalfOpenState: 3s # Half-Open ์ํ์์ ์ต๋ ๋๊ธฐ ์๊ฐ
slidingWindowType: COUNT_BASED # ์ฌ๋ผ์ด๋ฉ ์๋์ฐ ๋ฐฉ์: ํธ์ถ ํ์ ๊ธฐ์ค
slidingWindowSize: 10 # ์ต๊ทผ 10๋ฒ ํธ์ถ ๊ธฐ์ค์ผ๋ก ์คํจ์จ ๊ณ์ฐ
minimumNumberOfCalls: 5 # ์ต์ ํธ์ถ ํ์
waitDurationInOpenState: 60s # Open ์ํ์์ ๋๊ธฐ ์๊ฐ
๋๋ Micro Service ์ค ํ๋์ธ member-service๋ฅผ ํธ์ถํ๋ Feign Client์ CircuitBreaker๋ฅผ ์ ์ฉ์์ผฐ๋ค.
@FeignClient(name = "member-server")
public interface MemberServerClient {
@GetMapping("/member-server/api/v1/members/info")
@CircuitBreaker(name = "customCircuitBreaker", fallbackMethod = "getMemberFallback")
MemberInfoResponse getMemberInfo();
// Fallback ๋ฉ์๋ ์ถ๊ฐ
default MemberInfoResponse getMemberFallback(Throwable throwable) {
System.out.println("Fallback executed due to: " + throwable.getMessage());
return new MemberInfoResponse();
}
}
Fallback Method๋ Circuit Breaker๊ฐ Open ์ํ์ผ ๋ ํธ์ถ๋๋ ๋์ฒด ๋ฉ์๋์ด๋ค. ์๋ ํธ์ถํ๋ ค๋ ๋ฉ์๋์์ ์์ธ๊ฐ ๋ฐ์ํ๊ฑฐ๋ ์ธ๋ถ ์๋น์ค๊ฐ ์๋ตํ์ง ์์ ๋ Fallback ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์์ธ ์ํฉ์ ์ฒ๋ฆฌํ๊ณ , ์์ธ ๋ฐ์ ์ ์ด๋ค ๊ฐ์ ๋ฐํํ ๊ฑด์ง์ ๋ํด์๋ ์ค์ ํ ์ ์๋ค.
CircuitBreaker Closed ์ํ
member-server์ ์์ธ๊ฐ ๋ฐ์ํ๋๋ก ์ค์ ํ๊ณ ์์ฒญ์ ๋ณด๋ด๋ณด๋ฉด ์๋์ ๊ฐ์ด Fallback Method๊ฐ ํธ์ถ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๋ํ ๋น Response ๊ฐ์ฒด๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์๋ต๊ฐ์๋ null๋ก ์ฐํ๋ค.
๋น์ฐํ๊ฒ๋ member-server์ ๋ก๊ทธ๋ฅผ ํ์ธํด๋ณด๋ฉด ์๋ฌ๊ฐ ์ฐํ๋ค.
CircuitBreaker Open ์ํ์ผ ๋
๋๋ ์ต์ ํธ์ถ ํ์๋ฅผ 5๋ฒ์ผ๋ก ์ก์๊ธฐ ๋๋ฌธ์ 5๋ฒ ์ฐ์ ๋ณด๋ด๋ณด๋ฉด CircuitBreaker๊ฐ ์ด๋ฆฐ๋ค.
CircuitBreaker๊ฐ ์์ฒญ์ ์ฐจ๋จํ๊ธฐ ๋๋ฌธ์ member server์์ ๋์ด์์ ์๋ฌ ๋ก๊ทธ๋ ์ฐํ์ง ์๋๋ค.