본문 바로가기
프로그래밍/Spring Cloud

[MSA] Spring Cloud Hystrix - 개념편

by 사바라다 2020. 1. 6.

안녕하세요. 요즘 저는 MSA에 관하여 주로 포스팅을 올리고 있습니다. 오늘은 Spring Cloud에서 Circuit Break의 역할을 하는 Hystrix에 대해서 알아보도록 하겠습니다. 이번 포스팅은 이론을 중심으로 진행할 것이며 다음시간에는 Spring Boot를 통한 실습을 중심으로 진행하도록 하겠습니다.

Hystrix 개론

MSA 처럼 분산환경을 구성하다보면 서비스가 다양한 이유로 언젠가는 실패할 수 있습니다. 외부의 서버와 연결이 되지 않는다, 메모리 릭으로 인해 서버가 다운 된다. 등 다양한 이유로 말이지요. Hystrix는 이러한 장애상황에 대해 견딜 수 있도록해줍니다. 어떻게 장애 상황에 대해서 내성을 강화시켜 줄 수 있을까요? Hystrix는 아래와 같은 특징을 가지고 있습니다.

  1. 다른 서비스의 실패에 따른 내 서비스의 지연 또는 실패를 방지
  2. 분산 시스템의 복잡한 연쇄 실패 방지
  3. 빠르게 실패하고 빠르게 복구
  4. Fallback과 Gracefully degrade(완벽한 마무리)
  5. real-time에 유사한 모니터링과 알람

분산 시스템에서의 문제

출처 : https://github.com/Netflix/Hystrix/wiki

분산시스템에서는 Client가 여러 의존성(Network 등)을 가지게 됩니다. 만약 의존하고 있는 의존성 중 하나가 장애가 발생하여 지연이 발생하게 되면 어떻게 될까요? (위의 그림에서는 Dependency I)

대략적인 상황을 설명하면 아래와 같은 상황이 연출될 것입니다.

  1. 사용하고 있던 Client에 지속적인 Dependency I에 대한 요청이 들어옴.
  2. 그러던 중 Dependency I가 장애 발생(client에는 지속적인 요청이 들어오고 있음)
  3. Client에서 요청을 끊지 못하고 축적
  4. Client가 가지고 있던 thread pool의 양보다 더 많은 요청이 축적
  5. thread pool에서 처리하지 되어지지 않은 Packet들이 대기하는 상태 발생, CPU, 메모리 사용량 증가
  6. socket buffer에 요청온 패킷들이 쌓이기 시작
  7. 메모리 증가
  8. socekt buffer의 양보다 많이 쌓이게 되면 packet drop 발생
  9. Client 서비스 중단 위기

이렇게 연쇄적으로 상황이 악화됩니다. 우리는 이런 상황을 막을 필요가 있으며 Hystrix는 아래와 같이 시스템을 구성해서 위와 같은 상황을 막습니다.

Hystrix에서의 해결법

출처 : https://github.com/Netflix/Hystrix/wiki

Hystrix에서 위와 같은 상황을 방지하기 위해 취하는 방법은 위의 구성도와 같습니다. 좀 더 자세하게 나열 하면 아래와 같이 6가지로 축약할 수 있습니다.

  • Container(Tomcat 등)의 thread를 직접 사용하지 못하게 합니다.
  • Queue로 대기열을 사용하지 않고 빠르게 실패하게 합니다.
  • 실패로부터 서비스를 보호하기 위해 fallback을 제공하기도 합니다.
  • circuit-breaker 같은 isolation 기술을 이용하여 외부 장애의 영향을 최소화 합니다.
  • 실시간에 가까운 모니터링 및 경고 시스템을 통해 실시간 운영 수정을 수행할 수 있도록 합니다.
  • 네트워크뿐만 아니라 전체 종속성 CLient 실행의 장애로부터 보호합니다.

Hystrix flow

그러면 실질적으로 어떻게 구현되어 있는지 알아보도록 하겠습니다. hystrix의 flow는 아래와 같습니다.

출처 : https://github.com/Netflix/Hystrix/wiki

 

  1. HystrixCommand 또는 HystrixObservableCommand instance를 생성
  2. 생성한 instance 실행
  3. 응답에 대한 Cache 여부 확인
  4. Circuit Open 여부 확인
  5. Thread Pool/Queue/Semaphore 가 가득찼는지 확인
  6. 실제 명령어(function) 호출
  7. Circuit Health 체크
  8. Fallback 얻기
  9. 성공적인 응답 받기

바로 각각 순서에 대해서 구체적으로 알아보도록 하겠습니다.

HystrixCommand 또는 HystrixObservableCommand instance를 생성

dependency를 호출하는 곳에서 HystrixCommand와 같은 Hystrix관련 Instance를 생성합니다. 이 Class는 Circuit-breaker를 구현할 수 있도록 도와줍니다. 파라미터로는 실제 호출하는 method, 실패시 호출 될 fallback method를 넣습니다.

HystrixCommand command = new HystrixCommand(arg1, arg2);

생성한 instance 실행

HystrixCommand를 생성했으면 이제 실행해보도록 하겠습니다.

K value   = command.execute();

실행의 방법에는 execute, queue, observe, toObservable로 4가지가 있습니다. 이 4가지에 대한 자세한 설명은 사이트를 참고하시면 좋을것 같습니다.

응답에 대한 Cache 여부 확인

만약 해당 요청에 대해서 cache가 되어있다면 method를 실행하기 전에 바로 cache로 응답을 전달합니다.

Circuit Open 여부 확인

circuit이 열려있는지 확인하고 circuit이 열려있다면 실제 명령어를 호출하지 않고 Fallback method을 호출합니다.

  • Fallback method : 실제로 실행되는 method가 아닌 실패에 따라 호출되는 후처리 method

Thread Pool/Queue/Semaphore 가 가득찼는지 확인

Hystrix의 thread-pool과 그 queue, Semaphore를 호출 방식에 따라서 확인하고 만약 가득 찼다면 fallback method를 호출합니다.

실제 명령어(function) 호출

실제 명령어를 호출합니다. 실제 명령어에는 개인이 작성한 method일 수 있으며, dependency가 있는 method 일 수도 있습니다. 이때 timeout을 설정해 둘 수 있고 만약 실질 method가 설정해 둔 timeout을 넘으면 TimeoutException이 발생하고 오류가 발생하면 해당 오류를 원인으로 Fallback method를 실행합니다. 정상동작을하면 fallback method를 실행하지 않고 정상응답을 반환합니다.

Circuit Health 체크

hystrix는 각 명령어 호출에 대해 어떻게 처리되었는지를 circuit에 기록합니다. 이렇게 기록된 상태는 circuit을 오픈할지 안할지 결정하게 됩니다.

Fallback 호출

fallback method는 실패에 대해서 후처리를 위해 설정해 두는 method입니다. 해당 메서드는 Circuit Open, Thread pool의 Queue, Semaphore의 상태, 실제 명령어의 수행 여부 등에 따라서 호출 될 수 있습니다.

성공적인 응답 받기

정상적으로 처리됨에 따라 요청한 서비스에서 응답을 받을 수 있습니다.

circuit-breaker 구조

circuit-breaker는 fast-fail(바로 실패)의 여부를 정하는 회로같은 역할을 합니다. 이런 circuit-breaker는 hystrix 동작시 caching 확인후 바로 다음에 확인되며 열려 있을 경우 fallback method를 실행합니다. 어떤 경우에 circuit-breaker를 오픈하는지 작동 판단에 대해서 알아보도록 하겠습니다.

출처 : https://github.com/Netflix/Hystrix/wiki

 

circuit을 열지 닫을지의 판단은 일정 시간동안 일정량 이상의 요청에 대해서 어느정도의 실패율을 가지느냐에 따라 판단되어지며 열려있는 circuit은 특정시간이 지난 후 다시 1번 로직을 돌려 성공여부에 따라 열어둘지 다시 닫을 지 판단합니다.

Isolation 관련

Hystrix는 main thread와 호출되어지는 thread를 고립 시키기 위해 Thread방식과 Semaphore 방식 2가지를 사용할 수 있습니다. main Thread와 고립시킴으로써 영향을 최소화하기 위함입니다.

출처 : https://github.com/Netflix/Hystrix/wiki

Threads & Thread Pools

Main thread와는 별도의 thread-pool을 구성해서 해당 thread-pool을 자원으로 이용하여 사용하는 방법입니다. 위의 아키텍처에서는 왼쪽 2개를 표현하고 있습니다.

thrad-pool을 이용하면 아래와 같은 장점을 가지게 됩니다.

  • hystrix에 감싸져 있는 외부 시스템을 호출하는 부분은 완전히 main과 격리되게 됩니다. 이렇게 격리된 Thread-pool로 인해 main-thread-pool에는 영향을 주지 않습니다.
  • 새로운 의존성을 수용할 때 Risk를 최소화 할 수 있습니다. 문제가 발생되면 라이브러리와 분리되어 다른 모든 것에 영향을 미치지 않습니다.
  • 실패한 원격 서버가 다시 정상 상태가 되면 전체 Tomcat의 thread를 점유하고 있을 때 보다 thread pool의 정리가 빠르고 응용 프로그램이 즉시 정상적인 성능을 다시 시작합니다.
  • Hystrix의 thread pool을 이용하면 내장 된 동시성을 제공하여 동기 클라이언트 라이브러리 위에 비동기식으로 구성할 수 있습니다.

단점으로는 잦은 context switcing이 일어나기 때문에 전체적인 성능에 영향을 줄 수 있습니다. 빠르기는 Semaphore가 빠릅니다.

Semaphore

Semaphore 방법은 위의 그림에서 가장 왼쪽을 방법입니다. Thread 방법이 독립적인 thread-pool을 구성하여 독립성을 유지한다고 한다면 Semaphore 방법은 별도의 thread-pool이 아니라 main thread-pool에서 할당받아 사용하는 방법입니다.

thread 방식에 비해 속도 및 성능은 좋지만 격리성이 thread 보다 낮습니다. netpflix에서는 thread 방식을 권장하고 있습니다.

마무리

오늘은 이렇게 spring cloud hystrix의 이론에 대해서 알아보았습니다. hystrix는 method 호출을 고립시켜 장애에 대한 대응을 쉽게 가져갈 수 있도록 해준다는 것을 알았습니다. 다음시간에는 hystrix를 실습해보는 시간을 가지도록 하겠습니다.

감사합니다.

참조

https://github.com/Netflix/Hystrix/wiki

https://cloud.spring.io/spring-cloud-netflix/reference/html/#netflix-hystrix-dashboard-starter

http://woowabros.github.io/experience/2017/08/21/hystrix-tunning.html

https://design.codelytics.io/hystrix/how-it-works

댓글