IT/AWS

[Netflix] Hystrix Overview

물통꿀꿀이 2019. 1. 14. 22:16

Hystrix

Hystrix는 Netflix에서 만든 라이브러리로 분산 환경에서 fault tolerance을 수행하도록 한다. 이를 통해 Hystrix 라이브러리를 사용하는 서비스는 내부적으로 resiliency를 향상 시킬 수 있다. 

추가로 Hystrix는 Java 라이브러리로 관련 문서는 아래에서 확인 할 수 있다.

http://netflix.github.io/Hystrix/javadoc/


설계 원칙

Hystrix는 다음의 설계 원칙을 따른다.

- Dependency로부터 보호하고 latency 및 failure를 제어한다.

- 분산 환경에서 cascading failures(계단식 장애)를 중단한다. (끊는다)

- 빠르게 failure 시키고 복구한다.

- 대체 시스템(fallback)으로 자연스럽게 전환한다.

- 모니터링 및 경고 시스템 등을 가능하게 한다.


내부 동작 과정

Hystrix의 내부 코드 로직을 살펴보기 전에 중요한 부분만 간략하게 살펴보면 아래와 같다.

- dependencies 쪽으로 보내는 모든 요청은 command 형태(HystrixCommand, HystrixObservableComand)로 wrapping한다.

- command로 wrapping된 요청은 thread에서 동작한다.

- 제한된 시간내에 요청이 완료되지 못하면 timeout을 발생시킨다.

- thread-pool을 사용한다 (thread-pool이 가득차면 요청은 거절된다.)

- 요청의 success, failure, timeout, rejection과 같은 결과를 측정한다. (metric 측정)

- circuit-breaker 패턴을 적용한다.

- fallback 로직을 적용한다.


그림 1. Dependency Failure


이 전 글에서도 언급했었지만 다시 그림을 보면 Hystrix가 어떤 상황에서 필요한지 알 수 있다. 위의 그림 1을 보면 원활하게 잘 동작하다가 하나의 dependency에서 문제가 발생하면서 다른 요청들도 연달아 문제가 발생하고 있다. Netflix와 같은 사용자와 밀접하게 연결되는 서비스 상에서는 장애가 발생하면 그 여파는 이루 말할 수 없다. (돈도 그렇지만 서비스에 대한 신뢰도가 하락한다.)

그래서 위에서 간략히 언급했듯이 Hystrix를 통해 fault tolerance를 가능하게 한다.


그림 2. Hystrix Application


그림 2에서 볼 수 있듯이 dependency를 각각의 자원을 통해 접근하게 하여 하나의 dependency가 장애가 발생해도 다른 dependency에 영향을 주지 못하게 하였다. (중간에서 thread를 사용하여 각 dependency를 격리하였다.) 이 밖에도 장애가 발생할 때는 fallback 로직을 적용하여 사용자의 요청을 resilient하게 하였다.


그럼 아래에서는 실제 코드 상의 로직 flow을 살펴보려고 한다.


그림 3. Flow Diagram


위의 그림 3을 보면, Hystrix가 어떤 flow로 동작하는지 확인 할 수 있다. 그렇다면 Netflix에서 제공한 자료를 바탕으로 동작 순서를 간단히 살펴보도록 하자.

1. HystrixCommand 또는 HystrixObservableCommand 객체를 만든다. 이 객체를 통해 사용자는 request를 할 수 있다.

2. 1에서 만든 객체로 아래의 4가지 command를 사용 할 수 있다. 

- execute : block하고 나서 dependency에서의 응답을 반환한다. (dependency를 더이상 사용하지 못하게 막는다는 의미이다.)

- queue : dependency에서의 응답인 Future를 반환한다. (Future는 코드 상의 객체 이름이다.)

- observe : dependency에서 응답을 나타내는 Observable를 구독하고 반환한다. (Observable 또한 코드 상의 객체 이름이다.)

- observable : Hystrix command를 실행하고 받은 응답을 생략한 Observable를 반환한다. 

3. 응답이 캐시에 저장되어 있으면, Observable의 형태로 즉시 반환된다. (단, command 상에서 캐시가 enable 됬을 때)

4. circuit이 open되어 있는지 확인하여 open이면 8로 보내 장애 대응을 한다. 그렇지 않으면 5로 보낸다. (Circuit-breaker 패턴 적용)

5. thread-pool, queue 또는 semaphore가 가득차면, command를 실행하지 않고 8로 보내 장애 대응을 한다.

6. 아래의 메소드를 사용하여 dependency에 요청을 보낸다

- HystrixCommand.run() : 응답을 반환하거나 예외를 발생시킨다.

- HystrixObservableCommand.construct() : 응답을 생략하고 Observable를 반환하거나 onError 알람을 보낸다.

정상적으로 동작했을 때, Hystrix는 logging이나 metrix reporting을 수행한 후 응답을 받는다. 반면에 요청을 했지만 제한시간안에 정상 응답을 받지 못하면 timeout이 발생하여 8로 보내진다. (즉, 장애 대응 flow가 동작한다.)

7. Hystrix는 circuit breaker에 successes, failures, rejections, timeouts 과 같은 결과를 리포팅한다. circuit breaker는 리포팅된 결과를 바탕으로 circuit을 유지한다.

8. command가 실패해서 8번으로 들어올 때, Hystrix는 장애 대응을 한다. 

하지만 8번에서도 실패하는 경우는 발생한 예외가 아직 구현되지 않았거나 자체적으로 예외를 발생시키는 경우인데 이 때에도 Observable 객체를 반환한다. 또한 사용자에게 onError로써 요청이 실패했다는 알림을 전달한다. 

9. 마지막으로 사용자의 요청이 정상적으로 동작하면 아래의 그림 2와 같이 동작한다.



그림 4. Success Return

참고로 Hystrix의 모든 반환값은 Observable 객체로 반환되며, 사용자가 어떤 command로 요청을 전달했는지에 따라 실제 반환되는 값은 조금씩 다르다.

참조
https://github.com/Netflix/Hystrix/wiki
- https://github.com/Netflix/Hystrix/wiki/How-it-Works
- https://medium.com/netflix-techblog/introducing-hystrix-for-resilience-engineering-13531c1ab362