Hystrix 란 무엇인가

Dev. 로티·2022년 3월 2일
2

Spring boot

목록 보기
8/12
post-thumbnail

1. 개요

Order 서비스와 Product 서비스 이 두가지가 있다고 가정해보겠습니다.

사용자가 주문을 위해 주문 서비스로 요청중에 있습니다.
주문 서비스는 사용자의 주문 정보를 받아 상품 서비스(외부 서비스)에 주문 상품들에 대한 기본 금액과 수량 정보를 요청합니다.

여기서 만약 상품 정보를 요청하던 도중 상품 서비스의 지연이 발생하거나 혹은 장애가 발생할 경우 어떻게 될까요?

상품 서비스의 지연과 장애가 주문 서비스까지 더나아가 상품 서비스를 호출하는 다른 서비스들까지
영향을 끼치게되어 결국 연쇄적인 장애가 발생할 가능성이 높아져 서비스가 위험한 상황에 놓이게됩니다.



2. Hystrix란?

애플리케이션이 외부 시스템으로 인한 서비스의 지연과 장애에 대해 내성을 갖게 해주는 라이브러리로 Netflix에서 만들었으며, 현재는 유지보수만 진행하고 있다고 합니다.

Hystrix에서 지원하는 기능은 다음과 같습니다.

  • 빠른 실패(외부 시스템 혹은 서비스의 지연에 대한 빠른 실패처리)

  • 기본적으로 Thread Pool을 이용하여 포화 상태가 될 경우 앞으로의 요청은 실패 처리

  • 호출 실패에 대한 fallback 메소드 제공

  • 연쇄 장애 방지

  • Thread 혹은 Semaphore 전략에 따른 circuit breaker 기능

  • 실시간 모니터링



3. Hystrix 기본 동작 과정

  1. HystrixCommand 혹은 HystrixObservableCommand 객체를 생성

  2. Command 객체를 동작시킴(proxy의 역할)

  3. Cache의 여부를 확인

  4. Circuit breaker 확인(open[사용 불가 상태], close)

  5. 세마포어 혹은 Thread pool의 Thread를 사용할 수 있는 상태인지를 체크

  6. 실질적인 로직을 통작시킴(run() 메소드를 통해)

  7. Circuit breaker를 open 상태로 돌려야할지의 여부 체크

  8. 로직의 에러 혹은 circuit open 상태일 경우 빠른 실패 처리



4. Hystrix의 특징(Isolation)

Thread Pool
Command 객체가 Thread Pool 내에 있는 Thread에 의해 실행됩니다. 따라서 사용자에 요청에 대해 격리된 상태에서 로직을 수행 시키게됩니다.

Thread당 하나의 Command를 수행시키기 때문에 timeout 측정이 가능하고, 비동기 프로그래밍을 할 수 있습니다.

Semaphore
Command 객체를 수행하기 위해 몇개의 Thread만을 할당한 후 Semaphore를 이용해 동시 호출을 제한합니다.



5. Hystrix 기본 사용법

public class HystrixTestCommand extends HystrixCommand<String> {
    private final String world;

    public HystrixTestCommand(String world) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("testKey"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("testKey"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()));
        this.world = world;
    }

    @Override
    protected String run() throws Exception {
        return "hello!! " + world;
    }

    @Override
    protected String getFallback() {
        return "error!!";
    }
}  

run() : 실질적으로 동작시킬 로직
queue() : run()과 기능은 같지만 로직을 비동기적으로 수행
getFallback() : run() 에러 발생시 대체할 메소드
HystrixCommandGroupKey.Factory.asKey("testKey”) : Command의 그룹으로 모니터링시 같은 그룹끼리 그룹화됨
HystrixCommandKey.Factory.asKey("testKey”) : 해당 Hystrix Command를 식별하기 위한 값 


6. HystrixCommand vs HystrixObservableCommand

Blocking, Non-Blocking의 차이입니다.
HystrixCommand는 execute(), queue() 메소드를 사용할 수 있고,
HystrixObservableCommand는 observe(), toObservable()를 사용할 수 있습니다.



7. Hystrix Properties 정리

execution.isolation.strategy : HystrixCommand를 수행시킬 전략(THREAD, SEMAPHORE)

  • 만약 HystrixCommand를 사용하고 있다면 THREAD를 권장하고, HystrixObservableCommand를 사용하고 있다면 SEMAPHORE 사용을 권장하고 있습니다.

  • 또한 호출량이 많아 Thread의 오버헤드가 크게 발생할 경우에만 SEMAPHORE Isolation을 사용하라고 권장하고 있습니다.


[출처:https://github.com/Netflix/Hystrix/wiki/Configuration#fallback.isolation.semaphore.maxConcurrentRequests]

execution.isolation.thread.timeoutInMilliseconds : 해당 로직에 대한 timeout 설정합니다.

  • 만약 queue() 로 인해 로직이 수행될 경우 get()을 호출하지 않아도 queue()를 호출하는 시점에서 timeout이 측정됩니다.

execution.timeout.enabled : HystrixCommand.run()에 timeout을 지정할지의 여부를 설정합니다.

execution.isolation.thread.interruptOnTimeout : HystrixCommand.run() 수행 중 timeout이 발생시 Thread를 인터럽트 시킬지에 대한 여부를 설정합니다.

fallback.isolation.semaphore.maxConcurrentRequests : HystrixCommand.getFallback() 메소드가 수행될 수 있는 최대 요청 수를 의미합니다.

fallback.enabled : HystrixCommand.getFallback() 메소드의 사용 여부를 설정합니다.

circuitBreaker.enabled : circuit breaker를 통해 서비스 health check 및 빠른 실패처리를 진행할지의 여부를 설정합니다.

circuitBreaker.requestVolumeThreshold : 특정 시간 동안의 요청 수
(만약 20으로 설정되어있을 경우 circuit breaker open 여부를 측정하는 특정 시간 동안 19개가 실패했다면 circuit은 open되지 않음)

circuitBreaker.sleepWindowInMilliseconds : circuit이 open 되어있는 시간을 설정합니다.

circuitBreaker.errorThresholdPercentage : 설정한 시간동안 몇회 요청을 받는데에 있어 이 설정값에 설정한 퍼센트만큼 요청이 실패했다면 circuit을 open합니다.

circuitBreaker.forceOpen : 이 속성이 true인 경우 모든 요청은 빠르게 실패합니다. forceClose 보다 우선순위가 높습니다.

circuitBreaker.forceClose : 이 속성이 true인 경우 호출 퍼센트 상관없이 모든 요청을 빠르게 실패합니다.



8. HystrixCommand 사용시 주의할점

HystrixCommand strategy를 THREAD로 지정하게 되면 Thread Context가 달라지기 때문에 기존 Thread Context 값을 유지하지 못하는 문제가 발생합니다.

이때 Hystrix가 관리하는 Thread에서 부모 Thread Context 데이터를 공유하기 위해서는 아래와 같은 과정이 필요합니다.

  1. HystrixConcurrencyStrategy(com.netflix.hystrix.strategy.concurrency)를 상속 받아 작성한다.
    (HystrixConcurrencyStrategy란 Hystrix의 호출을 감싸 부모 Thread의 컨텍스트를 Hystrix가 관리하는 Thread에 주입시킬 수 있도록 도와주는 클래스입니다.)

  2. HystrixPlugins.getInstance().registerConcurrencyStrategy(...)에 작성한 클래스를 추가해 넣는다.

다음 포스팅에서는 Hystrix를 Spring Boot 환경에서 어떻게 사용하는지에 대해 알아보도록 하겠습니다.

0개의 댓글