Hystrix란
Netflix에서 Circuit Breaker Pattern을 구현한 라이브러리이다. Micro Service Architecture에서 장애 전파 방지를 할 수 있다.
Circuit Breaker Pattern
출처 : https://martinfowler.com/bliki/images/circuitBreaker |
장애 연쇄
위 그림에서 supplier 서버
에 장애가 생겨 항상 Timeout이 발생하는 경우, supplier 서버
를 호출한 client 서버
는 Timeout이 발생할 때까지 응답이 밀리게 된다. 응답이 밀리는 동안 요청이 계속 쌓여 결국 client 서버
까지 요청이 과하게 밀려 장애가 발생할 수 있다.
이러한 상황이 발생하지 않도록 circuit breaker
를 두어 장애 전파를 막을 수 있다.
출처 : https://cloud.spring.io/spring-cloud-netflix/multi/multi__circuit_breaker_hystrix_clients.html |
Hystrix Flow Chart
출처 : https://github.com/Netflix/Hystrix/wiki/How-it-Works |
HystrixCommand
,HystrixObservableCommand
객체 생성- Command 실행
- 캐시 여부 확인
- 회로 상태 확인
- 사용가능한 Thread Pool/Queue/Semaphore가 있는지 확인
HystrixObservableCommand.construc()
, 혹은HystrixCommand.run()
실행- 회로 상태 연산(Calculate circuit health)
- fallback 실행
- 응답 반환
Hystrix Circuit Breaker 구현
출처 : https://github.com/Netflix/Hystrix/wiki/How-it-Works |
- circuit health check를 위한 최소한의 요청이 있을 때(
HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()
) - 그리고, 지정한 오류율을 초과했을 때(
HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()
) - 회로의 상태를
CLOSED
에서OPEN
으로 변경 - 회로가 열린 동안, 모든 요청에 대해서 fallback method을 바로 실행
- 일정 시간이 지난 후(
HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()
), 하나의 요청을 원래 method로 실행(HALF OPEN
). 이 요청이 실패한다면OPEN
으로 두고, 이 요청이 성공한다면CLOSED
로 상태를 변경. 다시 1번으로 돌아감.
기본 설정
metrics.rollingStats.timeInMilliseconds
: 오류 감시 시간, 기본값 10초circuitBreaker.requestVolumeThreshold
: 감시 시간 내 요청 수, 기본값 20circuitBreaker.errorThresholdPercentage
: 요청 대비 오류율, 기본값 50
기본 설정을 풀어서 설명하면 다음과 같다
감시시간 내(30초)에, 20번 이상의 요청이 있었고, 그 중에서 오류율이 50% 이상일 때 Circuit Breaker가 작동한다(circuit open)
감시 시간 내에 요청이 반드시 20번 이상이 있어야 회로가 열림. 30초 동안 요청이 19번이었고 모두 실패했어도 Circuit Breaker는 작동하지 않는다
예제
dependencies
1 | dependencyManagement { |
사용
1 |
|
StoreIntegration.getStores()
가 실패하거나 회로가 열렸을 시에는.defaultStores
가 실행된다.
설정
application.yml로 설정예시
1 | hystrix: |
java 설정예시
1 | class UserResource { |
설정 참고:
https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.strategy
실사용례
Micro Service Architecture에서 하나의 게시판을 보여주는 예제(게시글, 댓글, 추천 게시글 API를 호출)
예제는 spring 4.3.x + reactor로 작성했다.
서비스
1 | 4j |
CommentClient.java
1 | public class CommentClient { |
- 여러 서비스를 호출하면서도, 하위 서비스의 장애가 client로 노출되지 않고, 성공한 서비스 응답들은 모두 client로 줄 수 있음
Isolation
bulkhead pattern를 채용하여 종속성(dependency)을 분리하며, 각각에 대한 접근을 제한했다.
출처 : https://github.com/Netflix/Hystrix/wiki/How-it-Works |
Threads & ThreadPool
호출 thread와 별도의 thread(ex. Tomcat thread pool)에서 동작한다.
출처 : https://github.com/Netflix/Hystrix/wiki/How-it-Works |
ThreadPool을 사용하지 않아도 되는 경우
- 네트워크 connection/read timeout, retry 옵션을 사용하여 매우 빨리 실패하거나
- client가 항상 정상동작한다는 신뢰가 있는 경우
즉, 그냥 ThreadPool을 사용하자
Netflix에서 각각의 Thread pool을 사용하여 의존성 격리를 구성한 이유
- 결론부터 먼저 말하자면, Thread를 나누어 다른 Thread에 접근하기 어렵도록 종속성을 원천차단한다.
- application은 수없이 많은 팀의, 수없이 많은 back-end service 를 수없이 많이 호출한다
- 각 service는 client library를 가지고 있다
- client library는 항상 바뀐다
- client library는 새로운 네트워크를 호출할 수도 있고, retry, parsing, caching 등의 logic을 가지며
blackbox
취급된다
출처 : https://github.com/Netflix/Hystrix/wiki/How-it-Works |
Thread Pool 사용상 이점
- application이 client library로부터 보호된다
- 덕분에 새 client library를 추가할 때의 risk를 낮출 수 있다, 장애는 격리된 thread에서 발생한다
Thread Pool 사용상 단점
- queueing, scheduling, context switching 등의 오버 헤드 발생가 발생된다(Netflix에서는 이를 사소한 정도로 간주)
Thread 비용
- Hystrix는 자식 thread에서
construct()
,run()
을 실행할 때, 부모 thread에서 총 종단 시간을 측정하여 overhead를 계산한다. - Netflix에서는 10억 건 이상의 Hystrix Command를 실행하며, 각 API 인스턴스마다 5-20개의 thread를 가지고 있는 thread pool을 40+개를 설정한다.(대부분의 thread pool 내의 thread 개수는 10개)
ThreadLocal
기본적으로 @HytrixCommand
는 다른 Thread로 동작을 하기 때문에, ThreadLocal이나 spring에서 지원해주는 @RequestScope
, @SessionScope
빈에 접근할 수 없다. 필요한 경우 execution.isolation.strategy: SEMAPHORE
로 변경하여 현재 Thread에서 연산을 실행하게 할 수 있다.
Spring Security를 사용하는 경우, hystrix.shareSecurityContext=true
로 설정해서 SecurityContext
를 공유할 수 있다.
THREAD 동작 방식의 경우에는 Thread-pool내의 Thread 갯수 만큼, SEMAPHORE 동작 방식의 경우에는 semaphore count 만큼 요청을 수행할 수 있다
execution.isolation.semaphore.maxConcurrentRequests
Semaphore
Thread pool을 사용하는 대신, Semaphore(counter)
를 사용하여 종속성에 대한 동시 호출 수를 제한할 수 있다. 따라서 Thread를 사용하지 않고 부하를 분한하지만, timeout과 격리가 느슨해지는 단점이 있다 위에서 ThreadPool을 사용하지 않아도 되는 경우
에서 설명한 것과 같이 back-end server를 신뢰할 수 있다면 사용해도 괜찮다
HystrixCommand
와 HystrixObservableCommand
는 두 곳에서 semaphore
를 지원한다
- Execution:
execution.isolation.strategy=SEMAPHORE
로 설정이 되어 있으면, 해당 command를 실행할 수 있는 부모 스레스 수를 제한 - Fallback 검색