본문 바로가기

IT Book Summary/스프링 마이크로 서비스 코딩공작소

Chapter 05 나쁜상황에 대비한 스프링 클라우드와 넷플릭스 히스트릭스의 클라이언트 회복성 패턴

 

서비스하나가 충돌할때 그것을 감지하기는 쉽지만

느려질때 성능 저하를 감지하고 우회하는 것이 어려운 이유

  1. 서비스 저하는 간헐적으로 발생 확산 가능
  2. 원격서비스 호출은 대개 동기식이며 오래 걸리는 호출을 중단하지 않음
  3. 애플리케이션은 대개 부분적 저하가 아닌 원격 자원의 완전한 장애를 처리하도록 설계됨
    (서비스가 완전히 실패하지 않으나 기능이 저하된채로 동작하게 됨)

 

5.1 클라이언트 회복성 패턴이란?

-> 원격 서비스가 에러를 던지거나 제대로 동작하지 못해 원격 자원의 접근 실패시, 
원격자원을 호출하는 클라이언트의 충돌을 막는데 초점을 맞춘다. 

 

회복성 패턴의 네가지

 

서비스 소비자와 서비스 사이에서 완충 역할을 하는 메가지 클라이언트 회복성 패턴

1 - 클라이언트 측 부하분산 client-side load balancing

-> 클라이언트가 넷플릭스 유레카 같은 서비스 디스커버리 에이전트를 이용해

서비스의 모든 인스턴스를 검색한 후 해당 서비스 인스턴스의 실제 위치를 캐싱하는 것.

 

서비스 소비자가 서비스 인스턴스를 호출 할 때마다 클라이언트 측 로드 밸런서는 

서비스 위치 풀에서 관리하는 서비스 위치를 하나씩 전달함.

 

클라이언트 로드밸런서는 중간에서 서비스 인스턴스가 불량인지 에러를 전달하는지를 감지해서 

문제가 되는 서비스 인스턴스를 제거하고 호출 전달을 막는다.

 

4장에서 설명한 리본라이브러리를 사용한 클라이언트 측 부하분산과 동일한 일이다.

 

2 - 회로 차단기 circuit breaker

-> 유입된 과전류를 차단하는 것처럼 전기 회로 차단기를 본떠 만든 회복성 패턴.

 

원격 서비스 호출을 모니터링 해 호출이 어느정도 실패하면 빨리 실패하게 만들어 더이상 호출되지 않도록 차단한다.

 

3 - 폴백 fallback

사용자 호출에 문제가 있어도 예외를 표시하지않고 나중에 해당요청을 수행할 수 있게 전달.

사용자 요청을 큐에 입력하는 작업과 연관된다.

 

ex) 사용자 행동 양식을 모니터링하고 구매가능성 있는 다른 물품들을 추천하는 기능을 제공하는 전자상거래 웹사이트의 경우.

선호도 서비스가 고장난다면 폴백은 일반화된 선호목록을 조회. 

 

4 - 벌크헤드 bulkhead

선체에 구멍이 뚫려도 구획으로 분리되어있어 침수 구역을 격리해 침몰을 예방하는 것처럼,

원격 자원에 대한 호출을 자원별 스레드 풀로 분리하여 어플리케이션이 다운되지 않도록 한다.

 

각 원격자원은 스레드 풀에 할당되고, 한 서비스 호출 스레드 풀이 포화되더라도 다른 서비스 호출은 포화되지 않는다


5.2 클라이언트 회복성이 중요한 이유

세 어플리케이션이 한두가지 방식으로 세가지 다른 서비스와 통신한다.

A,B는 서비스 A와 직접 통신.

서비스 A는 데이터베이스에서 데이터를 검색하고 서비스 B를 호출해 작업을 수행.

서비스 B는 환전히 다른 데이터베이스 플랫폼에서 데이터를 검색하고 서비스 C를 호출.

서비스 C는 외부 클라우드 제공업체의 서비스로 공유 파일시스템 데이터 내부 NAS 장치에 의존.

애플리케이션 C도 서비스 C를 직접 호출.

 

애플리케이션의 상호 연결된 의존성 그래프. 의존성 사이 원격호출을 관리하지 않으면 제대로 동작하지 않는 원격자원 하나가 모든 서비스를 다운시킬수 있음.

회로차단기 패턴이 분산자원을 호출하는곳에 구현된다면 스레드 부하를 피할 수 있다.

예를들어 서비스 C 호출에 회로차단기를 구현해 스레드를 소진하지 않게 빠르게 실패하도록 만들면 

서비스 B는 서비스 C 호출과 관련한 부분만 영향을 받고 나머지 기능에 대해서는 정상 작동하도록 한다.

 

문제가 있는 서비스 C 에 대해 회로차단기를 구현하고 생길 수 있는 세가지 시나리오

회로차단기가 오작동하는 서비스 호출을 원활하게 중단시킴

  1. 첫번째 성공적인 기본 시나리오
    회로차단기가 타이머를 설정하고 그 전에 호출이 완료되면 정상적으로 작업 수행
  2. 서비스 저하 시나리오. 폴백없는 회로차단기
    서비스 B가 회로차단기를 이용해 서비스C를 호출하지만 타이머가 만료되기전 호출이 완료되지않아 커넥션 차단.
  3. 서비스 저하에서 원활하게 회복하는 시나리오. 폴백있는 회로차단기
    계속 실패한 호출을 빠르게 실패시키고 대체 코드 폴백을 사용하여 다른곳의 데이터를 조회하게 만들수 있다. 
    서비스 C 호출을 막는동안 서비스 C는 복구할 여유가 생기며 성능 저하를 회복하고,
    회로차단기가 다시 간헐적으로 호출을 시도해 성공하면 회로차단기를 재설정하게 된다.

엔지니어가 직접적으로 서비스를 재시작하면서 발생하게되는 큰 문제들을 예방할 수 있다.

 


5.3 히스트릭스 시작

 

스레드 코드를 견고하게 짜는것은 어렵고 많은 작업이 필요하다.

-> 넷플릭스가 실전에서 검증한 히스트릭스를 사용하자.

 

https://github.com/carnellj/spmia-chapter5


5.4 스프링 클라우드와 히스트릭스를 위한 라이선싱 서버 설정

- 스프링 클라우드와 히스트릭스 랩퍼를 추가하도록 라이선싱 서비스의 메이블 빌드파일 구성

 

메이븐 의존성 설정.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

(스프링 부트2 버전으로 변경한후 Finchley.RELEASE를 사용하므로 hystrix-javanica를 별도 추가할 필요 없음.)

 

서비스 부트스트랩 클래스에 @EnableCircuitBreaker 추가

 


5.5 히스트릭스를 사용한 회로 차단기 구현

- 스프링 클라우드와 히스트릭스 애너테이션을 사용해 회로차단기 패턴으로 원격호출을 감싼다.

- 라이선싱 및 조직 서비스 모두 자기 데이터베이스에 대한 호출을 히스트릭스 회로 차단기에 연결

- 두 서비스 사이의 호출을 히스트릭스에 연결

 

히스트릭스는 원격 자원사이에 존재하며 클라이언트를 보호

 

@HystrixCommand 애너테이션을 사용해 히스트릭스 회로차단기가 관리하는 자바 클래스 메서드라고 표시한다.

스프링 프레임워크가 메서드를 감싸는 프록시를 동적으로 생성하고 원격 호출을 처리하기 위해 확보한

스레드가 있는 스레드 풀로 해당 메서드에 대한 모든 호출을 관리한다.

 

@HystrixCommand
public List<License> getLicensesByOrg(String organizationId){
  return licenseRepository.findByOrganizationId(organizationId);
}

 

메서드가 호출될때마다 회로차단기와 해당 호출이 연결.

메서드 호출이 1,000밀리초보다 오래 걸릴때마다 호출이 중단된다.

 

1 - 조직 마이크로 서비스에 대한 호출 타임아웃

메서드 수준 @HystrixCommand 애너테이션의 장점은

데이터베이스에 접근하거나 마이크로서비스를 호출하는데에도 쉽게 동일한 애너테이션을 사용하는것이다.

 

** 주의할 점 - 기본적으로 프로퍼티 설정없이 애너테이션을 지정하면

모든 원격 서비스 호출에 동일한 스레드풀을 사용하므로 문제를 일으킬 수 있다.

 

2 - 회로차단기의 타임아웃 사용자 정의

호출을 중단하기 전 시간을 어떻게 사용자 정의할 수 있는가?

-> 매개변수를 추가로 전달해 해결

 

@HystrixCommand(
  commandProperties={ @HystrixProperty(
  name="execution.isolation.thread.timeoutInMilliseconds", value="12000")
  })
public List<License> getLicensesByOrg(String organizationId){
  randomlyRunLong();
  return licenseRepository.findByOrganizationId(organizationId);
}

 

최대 타임아웃 시간을 12초로 설정하였다.

 

** 서비스 타임아웃

일부 서비스 호출이 다른 서비스 호출보다 느려지는 상황이 발생하면 서비스 호출을 스레드풀로 분리하는것 고려


5.6 폴백 프로세싱

- 회로차단기가 호출울 중단하거나 호출이 실패할 경우 폴백전략을 구현

- 중간자를 두어 서비스 실패를 가로채 다른대안을 선택할 기회를 줄 수 있다.

 

@HystrixCommand(fallbackMethod = "buildFallbackLicenseList") //호출이 실패할때 불러오는 클래스함수를 정의
  public List<License> getLicensesByOrg(String organizationId){
    randomlyRunLong();
    return licenseRepository.findByOrganizationId(organizationId);
  }
private List<License> buildFallbackLicenseList(String organizationId){ 
  List<License> fallbackList = new ArrayList<>();
  License license = new License()
    .withId("0000000-00-00000")
    .withOrganizationId( organizationId )
    .withProductName("Sorry no licensing information currently available");
  fallbackList.add(license);
  return fallbackList;
}

 

 @HystrixCommand 에 fallbackMethod 속성을 추가

실행 폴백메서드는 애너테이션으로 보호하려는 메서드와 동일 클래스에 있어야 한다.

 

** 폴백전략

- 성능 문제로 호출이 실패하면 실제 운영 데이터 저장소를 보호하기위해

데이터 웨어하우스 테이블에서 요약된 데이터를 검색하는 폴백을 사용하게 한다.

- 에러를 로깅하기 위해 호출 전후로 try catch 블록을 이용해 로깅 로직을 넣기도 한다.


5.7 벌크헤드 패턴 구현

- 서비스 내 개별 스레드풀을 사용해 서비스 호출을 격리하고, 호출되는 원격자원간에 벌크헤드 구축.

 

벌크헤드 패턴은 원격 자원 호출을 자신의 스레드 풀에 격리하기 때문에 컨테이너의 비정상 종료를 방지한다.

 

여러 유형 자원을 공유하는 히스트릭스 스레드 풀

호출량이 많은 서비스가 히스트릭스의 기본 스레드 풀의 모든 스레드를 차지하게 되므로 문제가 생길수 있다.

 

다음과 같이 벌크헤드(격벽) 으로 스레드가 분리되어있는 구조로 문제를 방지할 수 있다.

 

분리된 스레드 풀과 관련된 히스트릭스 명령

@HystrixCommand 애너테이션에 속성을 추가 

별도의 스레드 풀 설정, 스레드 풀 숫자 설정, 큐의 크기 설정.

 

@HystrixCommand(fallbackMethod = "buildFallbackLicenseList",
             threadPoolKey = "licenseByOrgThreadPool", //스레드풀 고유이름 설정
             threadPoolProperties = { // 스레드풀 동작을 정의하고 설정
                 @HystrixProperty(name = "coreSize",value="30"), //스레드 개수 정의
                 @HystrixProperty(name="maxQueueSize", value="10")}) //스레드풀 앞에 배치할 큐와 큐에 넣을 요청수 정의
public List<License> getLicensesByOrg(String organizationId){ 
  return licenseRepository.findByOrganizationId(organizationId);
}

 

maxQueueSize 속성에 대해 주의할것

-  값을 -1로 설정하면 유입된 호출을 유지하는데 SynchronousQueue 동기식 큐가 사용되고, 가용 스레드 수보다 더 많은 요청을 처리할수 있다. 1보다 큰 값으로 설정하면  LinkedBlockingQueue 사용해 더 많은 요청을 큐에 넣을수 있다.

- 스레드 풀이 처음 초기화될 때만 설정할 수 있음.

- 스레드 풀의 적정크기는? 

  (서비스가 정상일때 최고점에서 초당 요청 수 x 99 백분위 수 지연 시간(초)) + 오버헤드를 대비한 소량의 추가 스레드

 


5.8 히스트릭스 세부 설정

 

히스트릭스가 회로차단기 차단 시점을 결정하는 과정

 

차단여부를 결정하는 일련의 확인 과정

  1. 최소 요청수가 실패했는가?
    실패빈도 검사용 타이머 시작. 최소 호출횟수 초과시에만 회로 차단하고 빠르게 실패하게 만든다.
  2. 에러 임계치에 도달했는가?
    최소 호출 횟수를 넘고 전체 실패비율이 에러의 임계치에 도달하면 추가호출을 막는다.
  3. 원격 서비스 호출에 여전히 문제가 발생하는가?
    5초마다 호출을 허용하면서 호출성공시 회로차단기 초기화

@HystrixCommand 애너테이션의 commandPoolProperties 프로퍼디를 사용해 

 다섯가지 회로 차단기의 동작을 설정할 수 있다.

 

@HystrixCommand(
   fallbackMethod = "buildFallbackLicenseList",
   threadPoolKey = "licenseByOrgThreadPool",
   threadPoolProperties ={
     @HystrixProperty(name = "coreSize",value="30"),
     @HystrixProperty(name="maxQueueSize"value="10"),
   },
   commandPoolProperties ={
     //연속호출제어시간
     @HystrixProperty( name="circuitBreaker.requestVolumeThreshold", value="10"),
     //실패해야하는 호출비율
     @HystrixProperty( name="circuitBreaker.errorThresholdPercentage", value="75"),
     //서비스 회복상채를 확인할 때까지 대기할 시간 간격
     @HystrixProperty( name="circuitBreaker.sleepWindowInMilliseconds", value="7000"),
     //서비스 호출문제를 모니터할 시간간격
     @HystrixProperty(name="metrics.rollingStats.timeInMilliseconds", value="15000"), 
     //설정 시간간격동안 통계를 수집할 횟수
     @HystrixProperty(name="metrics.rollingStats.numBuckets", value="5")}
)
public List<License> getLicensesByOrg(String organizationId){ 
  logger.debug("getLicensesByOrg Correlation id: {}",
  UserContextHolder.getContext().getCorrelationId());
  randomlyRunLong();
  return licenseRepository.findByOrganizationId(organizationId);
}

 

1 - 히스트릭스의 구성 재검토

 

세가지 구성레벨

  • 애플리케이션 기본값
  • 클래스 기본값
  • 클래스안에서 정의된 스레드 풀 레벨

클래스 레벨 프로퍼티는 @DefaultProperties 애너테이션으로 설정

@DefaultProperties(
  commandProperties = {
    @HystrixProperty(
      name = "execution.isolation.thread.timeoutInMilliseconds", value = "10000")}
class MyService { ... }

 

** 운영시스템에서 설정 히스트릭스 데이터는 외부 스프링 클라우드 컨피그 서버에 저장한다.

 


5.9 스레드 컨텍스트와 히스트릭스

 

@HystrixCommand가 실행될때 THREAD와 SEMAPHORE라는 두가지 격리전략을 수행할 수 있다.

 

기본적인 THREAD 격리방법

히스트릭스가 자기통제하에서 호출을 시도한 부모스레드와 연관된 어떤 활동도 방재하지않고 스레드 실행을 중단시킴.

기본 격리전략인 THREAD 방식을 권장한다.

명령스레드와 부모스레드 사이 격리 수준을 높이며 SEMAPHORE 격리방식보다 무거움.

 

SEMAPHORE 격리방법

히스트릭스는 새로운 스레드를 시작하지않고 분산호출을 관리하며 타임아웃이 발생하면 부모스레드 중단시킴.

경량이고, 대용량을 처리하고 비동기 I/O 컨테이너에 적합.

SEMAPHORE 격리전략을 위해 격리수준을 설정하는 코드

@HystrixCommand(
commandProperties = {
  @HystrixProperty(
    name="execution.isolation.strategy", value="SEMAPHORE")})

 

1 - ThreadLocal 과 히스트릭스

기본적으로 부모스레드 컨텍스트는 히스트릭스과 관리하는 스레드에 전파되지 않음.

ThreadLocal에서 설정된 값은 기본적으로 부모스레드가 호출하는 메서드에 사용할수 없고 @HystrixCommand 객체로 보호됨.

 

스프링 필터를 사용해 REST서비스 모든 호출을 가로채고

HTTP요청 컨텍스트 정보를 추출해

사용자 정의 UserContext 객체에 저장.

UserContextHolder 클래스는 ThreadLocal 클래스에 UserContext를 저장하는데 사용.

 

https://github.com/carnellj/spmia-chapter5/blob/master/licensing-service/src/main/java/com/thoughtmechanix/licenses/utils/UserContextFilter.java

 

UserContext가 ThreadLocal에 저장되면 요청으로 실행되는 모든 코드에서 UserContextHolder의 UserContext객체를 사용가능.

 

https://github.com/carnellj/spmia-chapter5/blob/master/licensing-service/src/main/java/com/thoughtmechanix/licenses/utils/UserContextHolder.java

 

HTTP 헤더에 상관관계ID나 인증 토큰을 담아 전달후 하위서비스 호출에 전파

트랜잭션내 여러 서비스 호출을 추적할수 있게 고유 식별자 ID를 갖게됨.

 

상관관계 ID를 전달해 서비스를 호출하면 로그 메세지 3개가 나옴.

 

UserContext Correlation id: TEST-CORRELATION-ID

LicenseServiceController Correlation id: TEST-CORRELATION-ID

LicenseService.getLicenseByOrg Correlation:

 

히스트릭스가 보호하는 Licence-Service.getLicensesByOrder() 메서드에 컨텍스트가 전달되지않아 ID를 출력하지 않음.

 

2 - HystrixConcurrencyStrategy 동작

-> 히스트릭스와 스프링 클라우드는 부모 스레드의 컨텍스트를 히스트릭스 스레드 풀이 관리하는 스레드에 전달하는 매커니즘

 

히스트릭스 병행성 전략 클래스 사용자 정의

기본적으로 하나의 HystrixConcurrencyStrategy만 정의할 수 있다.

 

스프링 클라우드는 이미 보안정보를 전달하기 위해 병행성 전략을 정의하고 있는데,

히스트릭스의 병행성 전략에 플러그인해 연결해 사용한다.

 

public class ThreadLocalAwareStrategy extends HystrixConcurrencyStrategy{
    private HystrixConcurrencyStrategy existingConcurrencyStrategy;

    public ThreadLocalAwareStrategy(
            HystrixConcurrencyStrategy existingConcurrencyStrategy) { // 미리정의한 병행성 클래스 전달
        this.existingConcurrencyStrategy = existingConcurrencyStrategy;
    }

    @Override
    public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
        //부모 메서드 호출하여 재정의
        return existingConcurrencyStrategy != null
                ? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize)
                : super.getBlockingQueue(maxQueueSize);
    }

    @Override
    public <T> HystrixRequestVariable<T> getRequestVariable(
            HystrixRequestVariableLifecycle<T> rv) {
        return existingConcurrencyStrategy != null
                ? existingConcurrencyStrategy.getRequestVariable(rv)
                : super.getRequestVariable(rv);
    }

    @Override
    public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                                            HystrixProperty<Integer> corePoolSize,
                                            HystrixProperty<Integer> maximumPoolSize,
                                            HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
                                            BlockingQueue<Runnable> workQueue) {
        return existingConcurrencyStrategy != null
                ? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize,
                maximumPoolSize, keepAliveTime, unit, workQueue)
                : super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,
                keepAliveTime, unit, workQueue);
    }

    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        return existingConcurrencyStrategy != null
                ? existingConcurrencyStrategy
                .wrapCallable(new DelegatingUserContextCallable<T>( // UserContext를 설정할 Callable 구현체를 주입
                callable, UserContextHolder.getContext()))
                : super.wrapCallable(new DelegatingUserContextCallable<T>(
                callable, UserContextHolder.getContext()));
    }
}

이미 스프링 클라우드가 이미 HystrixConcurrencyStrategy를 정의하기 때문에 

재정의 할 수 있는 모든 메서드는 기존 병행성 전략 메서드의 존재 여부를 확인한 후 적절히 호출해야 함.

이것을 고려하지 않으면 히스트릭스가 보호하는 코드 안에서 스프링 보안 컨테스트를 사용하려고할때 문제가 발생할 수 있다.

 

히스트릭스 명령에 UserContext 주입하도록 자바 Callable 클래스 정의

부모 스레드의 컨텍스트를 히스트릭스 명령에 전파하는 작업을 수행할 Callable 클래스 구현

 

public final class DelegatingUserContextCallable<V> implements Callable<V> {
    private final Callable<V> delegate;
    private UserContext originalUserContext;

    public DelegatingUserContextCallable(Callable<V> delegate,
                                             UserContext userContext) {
        this.delegate = delegate;
        this.originalUserContext = userContext;
    }

    public V call() throws Exception { //메서드를 보호하기전 호출되는 Call함수
        //UserContext가 설정. ThreadLocal변수는 히스트릭스가 보호하는 메서드를 실행하는 스레드에 연결됨.
        UserContextHolder.setContext( originalUserContext );
        try {
            return delegate.call(); //히스트릭스가 보호하는 call() 메서드 호출
        }
        finally {
            this.originalUserContext = null;
        }
    }

    public static <V> Callable<V> create(Callable<V> delegate,
                                         UserContext userContext) {
        return new DelegatingUserContextCallable<V>(delegate, userContext);
    }
}

히스트릭스로 보호된 메서드를 호출하면

 

히스트릭스와 스프링 클라우드는 DelegatingUserContextCallable 클래스 인스턴스를 만들고

보통 히스트릭스 명령풀에서 관리하는 스레드에서 호출될 Callable 클래스를 전달한다.

 

Callable 클래스는 delegate 자바 프로퍼티에 저장됨.

 

스프링 클라우드는 호출을 시작한 부모 스레드에서 받은 UserContext객체를 함께 전달.

 

실제작업은 DelegatingUserContextCallable 같은 사용자 클래스의 call() 메서드에서 일어남.

 

call() 메서드에서 먼저 UserContextHolder.setContext() 메서드를 사용해 UserContext를 설정.

 

setContext() 메서드는 실행중인 스레드별 ThreadLocal 변수에 UserContext 객체를 저장.

 

UserContext를 설정한 후 위임된 Callable 클래스의 call() 메서드 호출.

 

delegate.call()을 호출하면 @HystrixCommand 애너테이션이 보호하는 메서드가 호출.

 

히스트릭스 병행성 전략을 사용자 정의하기위해 스프링 클라우드 구성

ThreadLocalConfiguration 구성 클래스를 정의

 

@Configuration
public class ThreadLocalConfiguration {
  @Autowired(required = false)
  private HystrixConcurrencyStrategy existingConcurrencyStrategy;

  @PostConstruct
  public void init() {
  //개로운 병행성 전략 등록위해 모든 히스트릭스 컴포넌트에 플러그인 재설정
    HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance()
                                               .getEventNotifier();
    HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance()
                                                    .getMetricsPublisher();
    HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance()
                                                       .getPropertiesStrategy();
    HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance()
                                                          .getCommandExecutionHook();
    HystrixPlugins.reset();

    HystrixPlugins.getInstance().registerConcurrencyStrategy(
    new ThreadLocalAwareStrategy(existingConcurrencyStrategy)); //플러그인에 등록
    HystrixPlugins.getInstance().registerEventNotifier(eventNotifier); //히스트릭스 컴포넌트 재등록 
    HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
    HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
    HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
  }
}

 

서비스 안에서 실행되는 모든 컴포넌트를 관리하는 히스트릭스 플러그인을 다시 구축함.

init()메서드로 레퍼런스 가져와 사용자정의 등록

 

스레드 수준의 격리방식으로 히스트릭스를 사용하기 때문에 많은 작업이 필요하다.


  • 마이크로서비스 기반을 둔 애플리케이션처럼 고도로 분산된 애플리케이션을 설계할 때는 클라이언트 회복성이 고려되어야 함
  • 성능이 나쁜 서비스 하나가 호출을 완료할 때까지 호출 클라이언트를 대기시키므로 연쇄적인 자원고갈을 유발함
  • 세가지 핵심적인 클라이언트 회복성패턴 - 회로차단기와 폴백, 벌크헤드 패턴
  • 회로차단기 패턴은 느리게 실행되고 성능 저하된 시스템 호출을 빨리 실패시키고 자원고갈을 방지
  • 폴백패턴을 사용하면 원격 서비스 호출이 실패하거나 호출에 대한 회로 차단기가 실패할 때 대체할 코드 경로를 정의가능
  • 벌크헤드 패턴은 원격호출을 서로 격리하고 원격서비스 호출을 자체 스레드 풀로 분리함
  • 스프링 클라우드와 넷플릭스 히스트릭스 라이브러리는 회로차단기와 폴백, 벌크헤드 패턴에 대한 구현을 제공
  • 히스트릭스 라이브러리는 구성 기능이 뛰어나며, 애플리케이션 전역, 클래스, 스레드풀 레벨로 설정.
  • 히스트릭스는 THREAD, SEMAPHORE 격리 모델을 지원
  • THREAD 모델은 히스트릭스로 보호된 호출을 완벽히 격리해서
    부모 스레드 컨텍스트를 히스트릭스가 관리하는 스레드에 전파하지 않음.
  • SEMAPHORE 모델은 이스트릭스 호출을 위해 별도의 스레드를 사용하지 않음.
    더 효율적인 모델이지만 호출을 중단할때 서비스가 예상하지 않은 동작이 유발될 수 있음.
  • 히스트릭스를 사용하면 사용자 정의 HystrixConcurrencyStrategy를 구현해 부모 스레드 컨텍스트를 히스트릭스가 관리하는 스레드에 주입할 수 있다.