resilience4j
MSA íê²½ìì ìí·ë¸ë ì´ì»¤, ì ëì ì´ë ë§¤ì° ì¤ìí ê°ë ì¼ë¡ ë³´ë¤ ë ìì í ìë¹ì¤ ì´ìì ìí´ ì¬ì©ëë ê°ë ì´ë¤.
ìë¹ì¤ê° íì¥ëë©´ì ê°ì í ë´ìì ë¶ë¦¬ë ìë¹ì¤ê° í 쪽ì¼ë¡ ìì¡´(í¸ëí½)ì íë©´ì Aì ìë²ê° Bì API를 ë¹ëê¸°ë¡ í¸ì¶íë©´ì ë°ì´í°ë¥¼ ê°ì ¸ì¤ë ìí©ì´ ë°ìíë¤.
Aì ìë²ë¥¼ ê°ë°íë ëë ëª ê°ì§ ìí©ì ëë¹í´ì¼íë¤.
- Bì ìë²ê° 죽ì´ìë¤ë©´?
- Bì ìë²ê° CPU ìì ë¶ì¡±ì¼ë¡ 죽기 ì§ì ì´ë¼ë©´?
- Bì ìë²ê° ìê°ì ì¸ ìë¬ë¥¼ ë±ëë¤ë©´?
ì¬ê¸°ì Aìë²ë Bì ìë²ê° íë³µí ì ìëë¡ ê¸°ë¤ë ¤ì¤ê³¼ ëìì ì¥ì ê° ì íëì§ ìëë¡ ë§ìì¼íë¤. ì¬ê¸°ì ë´ê° ê°ì ¸ì¬ ì ìë ê²ì ìí·ë¸ë ì´ì»¤ì´ë¤.
ìí·ë¸ë ì´ì»¤ë?

ì§ìì ì¬ì©íë ë꺼ë¹ì§ì´ë¼ ë¶ë¦¬ë ëì ì°¨ë¨ê¸°, íë¡ì°¨ë¨ê¸°ê° ìí·ë¸ë ì´ì»¤ì´ë¤.
ê°ë°ìë¤ ì¸ê³ìì ì¬ì©íë ìí·ë¸ë ì´ì»¤ë ê°ê°ì ìì¤í ê° ì¥ì ê° ì íëì§ ìëë¡ ì°¨ë¨ì 목ì ì¼ë¡ ëë ê²ì ë§íë¤. ìí·ë¸ë ì´ì»¤ë ìíì ë°ë¼ì ìë¡ ë¤ë¥¸ ëìì íë¤.
ìí·ë¸ë ì´ì»¤ì ìí

ìí·ë¸ë ì´ì»¤ì ìíë ë³´íµìíì í¹ë³í ìíê° ìë¤.
ë³´íµìíìì 3ê°ë¡ ë¶ë¥ê° ëë¤.
- CLOSED: ì ìì ì¼ë¡ í¸ì¶ëê³ ìëµì 주ë ìíë¡ ì¤í¨ì¨ì´ë ë린 í¸ì¶ ë¹ì¨ì´ ìê³ê°ì ëì§ ìì ê²½ì°ì´ë¤.
- OPEN: 문ì ë°ìì´ ê°ì§ë ìí, ì¼ì 기ê°ëì ë무 ë§ì ìì²ì´ ì¤í¨íê±°ë ë무 ëë¦¬ê² ìëµíë ê²½ì° ìí· ë¸ë ì´ì»¤ê° ì´ë ¤ ëì´ì ìì²ì ë§ì ìíì´ë¤.
- HALF_OPEN: 문ì ë°ìì´ ê°ì§ë ìí, ìí· ë¸ë ì´ì»¤ê° ì¼ì ìê°ì´ ì§ë í ë¤ì ë«íì§ ì´ë¦´ì§ ê²°ì í기 ìí´ ì¼ë¶ ìì²ì íì© íë ìí
í¹ë³í ìíë 2ê°ë¡ ë¶ë¥ëë¤.
- DISABLED: íì í¸ì¶ì íì©íë ìí
- FORCE_OPEN: íì í¸ì¶ì ê±°ë¶íë ìí
ìí· ë¸ë ì´ì»¤ë ì¬ë¼ì´ë© ìëì°(Sliding Window)를 ì¬ì©íì¬ ìíì ë³íì¬ë¶ë¥¼ ê²°ì íë¤. ì¬ë¼ì´ë© ìëì°ë íì ë°©ì(COUNT-BASED)ê³¼ ìê° ë°©ì(TIME_BASED)ê° ìëë° ë°©ìì ë°ë¼ ì¬ë¼ì´ë© ìëì° ììì ì í´ì§ íë¥ ë³´ë¤ ëì íë¥ ë¡ í¸ì¶ì ì¤í¨íê² ëë©´ ìí를 OPENì¼ë¡ ë³ê²½íë¤.
OPEN ìíììë ì°ëë ìì¤í í¸ì¶ì ìëíì§ ìì¼ë©°, ë°ë¡ í¸ì¶ ì¤í¨ Exceptionì´ ë°ìíì¬ ì í´ì§ Fallbackëìì ìííë¤.
OPEN ì´í ì¤ì í ìê°ì´ ì§ëë©´ HALF_OPEN ìíë¡ ë³ê²½ëë©° í¸ì¶ì´ ì ìíëìëì§ ë¤ìíë² ì¤í¨ íë¥ ë¡ íì¸íë¤. ì ìíëìë¤ê³ íëë©´ CLOSED ìíë¡ ë³ê²½ëì§ë§ ìì§ ì ìíëì§ ëª»íë¤ê³ íë¨ëë©´ ë¤ì OPEN ìíë¡ ëëìê°ë¤.
ìí·ë¸ë ì´ì»¤ ë¼ì´ë¸ë¬ë¦¬
ìí· ë¸ë ì´ì»¤ë¥¼ ì²ë¦¬í ì ìê² ëì주ë ë¼ì´ë¸ë¬ë¦¬ë ëíì ì¼ë¡ 4ê°ê° ìë¤.
- Resilience4J
- Spring Cloud Circuit Breaker
- Hystrix
- Sentinel
ì´ ì¤ ìµê·¼ìë 커ë°ì´ ì°íìì¼ë©° íë°íê² ì¤íìì¤ë¡ ê°ë°ì´ ì´ë£¨ì´ì§ë ê²ì Resilience4Jì´ë¤. ë°ë¼ì, Resilience4J를 ì ííìë¤.
https://github.com/resilience4j/resilience4j
Resilience4Jë?
Resilienceë íë³µ íë ¥ì±ì´ë¤. 4ë For Jë Java
Resilience For Java ì¦, íë³µ íë ¥ì±ì ìë°ë¥¼ ìí´ ë§ë¤ì´ì§ ë¼ì´ë¸ë¬ë¦¬ì´ë¤. ì°ë¦¬ë ì¬ê¸°ì íë³µ íë ¥ì±ì ì§ì¤í´ì¼íë¤. ìì¤í ì ëë ì´ ë³µì¡í´ì ¸ ê°ë©´ì ê³ ê°ë¤ìê² ê³ ê°ì©ì±ì ì ì§í´ì¼íê³ ì¥ì ë ì íê° ëì§ ìëë¡ í´ì¼íë¤. ê·¸ë ë¤ë©´ ìì¡´ì±ì´ ìë ìì¤í ë¼ë¦¬ë ìë¡ ì¥ì ê° ì¼ì´ëì§ ìëë¡ ì°ì°ìì (í¸ëí½)ì ëí´ì ë³´ë¼ì§ ë§ì§ë¥¼ ê²°ì íì¬ í¸ëí½ì ë°ë ìì¤í ìì ììì´ ë¶ì¡±í ìí©ìì ë¤ì´ì´ ëë ìí©ì´ ë°ìíì§ ìëë¡ ë§ìì¼íë¤. ì¬ê¸°ì ë¤ì íë³µì í ì ìëë¡ íë ê²ì´ íë³µíë ¥ì±ì´ë¤.
Resilience4J ì ì©í기
Getting Started를 íì¸íì¬ ì ì©í기 (Java 17 ì´ìì ì구íë¤.)
https://resilience4j.readme.io/docs/getting-started
//Resilience4J
implementation("io.github.resilience4j:resilience4j-spring-boot3:2.2.0")
configë bootì application.yml í¹ì application.properties를 íµí´ ì¤íë§ ë¹ì ì¤ì ê°ì 주ì í ì ìë¤.
resilience4j:
circuitbreaker:
configs:
default:
registerHealthIndicator: true
slidingWindowType: COUNT_BASED
slidingWindowSize: 10
minimumNumberOfCalls: 5
slowCallRateThreshold: 100
slowCallDurationThreshold: 60000
failureRateThreshold: 60
permittedNumberOfCallsInHalfOpenState: 3
waitDurationInOpenState: 60s
automatic-transition-from-open-to-half-open-enabled: true
instances:
circuit-sample-common: # circuitbreaker name
baseConfig: default # 기본 config ì§ì
circuit-sample-3000:
baseConfig: default
slowCallDurationThreshold: 3000 # ìëµìê°ì´ ë린ê²ì¼ë¡ íë¨í ê¸°ì¤ ìê° [ms]
SlidingWindowType
SlidingWindowTypeì COUNT_BASED, TIME_BASEDê° ìë¤. ê³µì문ìì ìíë©´ ëì ìê° ë³µì¡ëë O(1)ì´ê³ ê³µê° ë³µì¡ëë O(WindowSize)ì´ë¤.
COUNT_BASED: ë§ì§ë§ ì¬ë¼ì´ë© WindowSize í¸ì¶ì´ 기ë¡ëê³ ì§ê³ëë¤.
TIME_BASED: ë§ì§ë§ ì¬ë¼ì´ë© WindowSize ì´ì í¸ì¶ì´ 기ë¡ëê³ ì§ê³ëë¤.
slidingWindowSize
ìí·ì ìíê° CLOSEDì¼ ë ìì²ì 결과를 기ë¡í기 ìí ì¬ë¼ì´ë© ìëì°ì í¬ê¸°
minimumNumberOfCalls
ìí·ì´ ì¤í¨ì¨ ëë ì§ì°ë ìëµì ê³ì°í기 ì ì구ëë ìµì ìì²ì ì, ììììë ìµì 5ê°ì ìì²ì´ ìì´ì¼ ìí·ì´ ê³ì°ì íë¤.
slowCallRateThreshold
ì§ì°ë ìëµ Threadhold í¼ì¼í¸ ê°ì¼ë¡ ë린 í¸ì¶ì ë°±ë¶ì¨ì´ ìê³ê°ê³¼ ê°ê±°ë í¬ë©´ CircuitBreakerê° ê°ë°©ì¼ë¡ ì íëê³ ë¨ë½ í¸ì¶ì´ ììëë¤.
slowCallDurationThreshold
ìì²ì´ ë린 ê²ì¼ë¡ ê°ì£¼ëë 기ê°
failureRateThreshold
ì¤í¨ì¨ Threadhold í¼ì¼í¸ ê°ì¼ë¡ ì¤í¨ì¨ ìê³ê°ë³´ë¤ í¬ê±°ë ê°ì¼ë©´ CircuitReakerê° ê°ë°©ì¼ë¡ ì ííê³ ë¨ë½ í¸ì¶ì ììíë¤.
permittedNumberOfCallsInHalfOpenState
ìí·ì´ HALF_OPENìíì¼ ë íì©ëë í¸ì¶ì를 ì미íë¤.
waitDurationInOpenState
ìí·ì ìíê° OPENìì HALF_OPENì¼ë¡ ë³ê²½ë기 ì ì Circuit Breakê° ê¸°ë¤ë¦¬ë ìê°ì´ë¤. ììììë 20ì´ ì´íì ë³ê²½ëë¤.
automatic-transition-from-open-to-half-open-enabled
true/falseë¡ Wait Duration In Open State기ê°ì´ ì§ë ì´íì Openìì Half-Openì¼ë¡ ìëì¼ë¡ ìíê° ë³ê²½ëë¤. íëì ì°ë ëê° CircuitBreakerì 모ë ì¸ì¤í´ì¤ë¤ì 모ëí°ë§íë©° ìí를 íì¸íë¤.
Resilience4J ìí© ìì°
@Slf4j
@Service
public class AService {
@CircuitBreaker(name = "circuit-sample-common", fallbackMethod = "fallback2")
public String callOutService() {
throw new RuntimeException("ìë¬ë°ì!");
}
private String fallback2(CallNotPermittedException e) {
log.error("Circuit breaker is open {}", e.getMessage());
return "fallback";
}
}
OUTPUT
2024-08-02T13:56:42.277+09:00 ERROR 84435 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.RuntimeException: ìë¬ë°ì!] with root cause
java.lang.RuntimeException: ìë¬ë°ì!
2024-08-02T13:56:43.324+09:00 ERROR 84435 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.RuntimeException: ìë¬ë°ì!] with root cause
java.lang.RuntimeException: ìë¬ë°ì!
2024-08-02T13:56:44.361+09:00 ERROR 84435 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.RuntimeException: ìë¬ë°ì!] with root cause
java.lang.RuntimeException: ìë¬ë°ì!
2024-08-02T13:56:45.406+09:00 ERROR 84435 --- [nio-8080-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.RuntimeException: ìë¬ë°ì!] with root cause
java.lang.RuntimeException: ìë¬ë°ì!
2024-08-02T13:56:46.455+09:00 ERROR 84435 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.RuntimeException: ìë¬ë°ì!] with root cause
java.lang.RuntimeException: ìë¬ë°ì!
2024-08-02T13:56:47.493+09:00 ERROR 84435 --- [nio-8080-exec-6] com.example.resilience.a.AService : Circuit breaker is open CircuitBreaker 'circuit-sample-common' is OPEN and does not permit further calls
ì¤ì í´ë ìê³ì¹ì¸ 5í를 ë기ì ê·¸ëë¡ fallbackMethod를 ì¤ííê³ ìë¤.
íì¬ ì¤ì ììë ìµì 5ê°ì í¸ì¶ì´ ë°ìíê³ ê·¸ ì¤ 3ê°(60%)ê° ì¤í¨í ê²½ì° ìí·ì´ OPEN ìíë¡ ë³ê²½ëë¤. ìí·ì´ ì´ë¦¬ë©´ ì¤ì ë ìê°(60ì´) ëì ìë¡ì´ í¸ì¶ì ì°¨ë¨íë¤. ì´í ìí·ì Half-Open ìíë¡ ì íëê³ ì¼ë¶ í¸ì¶ì íì©íì¬ ìì¤í ì ìí를 íì¸íë¤.
주ì Catchí ìë¬ë CircuitBreakerì í¬í¨ëì§ ìëë¤.
CircuitBreaker를 ì ì©í ë¶ë¤ì ì§ìì íìê² ì§ë§, CircuitBreakerì ë´ë¶ë AOP를 íµí´ 구íëì´ ììµëë¤. ë°ë¼ì, í´ë¹ ë©ìëìì ë°ìíë catchê° ì´ë£¨ì´ì§ ê²½ì°, CircuitBreakerë í¬í¨ëì§ ììµëë¤.
@CircuitBreaker(name = "circuit-sample-common", fallbackMethod = "fallback2")
public String callOutService() {
try {
throw new RuntimeException("ìë¬ë°ì!");
}catch (Exception e) {
log.error("ìë¬ë°ì: {}", e.getMessage());
return "catch error";
}
}
모ëí°ë§
https://resilience4j.readme.io/docs/micrometer 매í¸ë¦ ì ë³´ë í´ë¹ ì¬ì´í¸ìì íì¸ ê°ë¥íë¤. (본 ê¸ì Spring Actuator를 íµí´ íì¸)
https://resilience4j.readme.io/docs/grafana-1 í´ë¹ ì¬ì´í¸ê°ì íì¸ì íë¤.
grafana JSON íì¼ ë§í¬ https://resilience4j.readme.io/docs/grafana-1

ìí·ë¸ë ì´ì»¤ê° ìëíë ì¼ì´ ìëë¡....
Reference.
CircuitBreaker
Getting started with resilience4j-circuitbreaker
resilience4j.readme.io