[MSA] Hystrix를 사용한 Circuit Breaker Pattern 적용

Dawon Seo·2022년 8월 26일
0

MSA

목록 보기
3/4
post-thumbnail

Circuit Breaker Pattern

MSA 패턴은 시스템을 여러 개의 서비스 컴포넌트로 나누어 서비스간 통신을 하는 개념을 가지고 있습니다.
이러한 구조의 특성상 한 서비스에 장애가 생긴다면, 그 컴포넌트를 호출하는 서비스에까지 장애가 전파되는 단점을 가지고 있기도 합니다.
이러한 문제를 해결하는 디자인 패턴이 바로 Circuit Breaker입니다.

Service 1 -> Circuit Breaker -> Service

위와 같이 서비스와 서비스 호출 사이에 Circuit Breaker를 두는 개념으로, Service 1에서 Service 2로의 모든 호출은 Circuit Breaker를 통하게 됩니다.
만약 Service 2에 장애를 감지한다면 Service 2로의 호출을 강제로 끊어 장애가 전파되는 것을 방지하는 것이죠!

1. 빠른 실패(fail fast): 원격 서비스가 저하를 겪으면 애플리케이션은 빨리 실패함으로써 애플리케이션 전체를 다운시킬 수 있는 자원 고갈 이슈를 방지합니다.
2. 원만한 실패(fail gracefully): 애플리케이션 개발자는 원만하게 실패하거나 사용 의도로 수행하는 대체 매커니즘을 찾을 수 있습니다.
3. 원활한 회복(recover seamlessly): 회로 차단기는 요청 자원이 온라인 상태인지 주기적으로 확인하고, 사람의 개입 없이 자원 접근을 다시 허용할 수 있습니다.


fallback 처리

  • 폴백 패턴을 사용하면 원격 서비스에 대한 호출이 실패할 때 예외(exception)를 발생시키지 않고 서비스 소비자가 대체 코드 경로를 실행해 다른 방법으로 작업을 수행할 수 있습니다.
  • 일반적으로 이 패턴은 다른 데이터 소스에서 데이터를 찾거나 향후 처리를 위해 사용자 요청을 큐(queue)에 입력하는 작업과 연관됩니다.

Hystrix

  • Netflix가 제공하는 컴포넌트로, Circuit breaker 패턴을 자바 기반으로 오픈소스화한 라이브러리입니다.
  • 서비스가 장애 내성, 지연 내성을 갖도록 도와줄 뿐만 아니라 모니터링 기능도 제공합니다.

그렇다면 직접 Hystrix를 사용하여 Circuit Breaker Pattern을 적용해 보고, 폴백 전략까지 구현해 봅시다~
ecommerce 프로젝트의 order 서비스에서 customer 서비스 컴포넌트로 사용자의 이름을 요청하는 경우를 예제로 설정합니다.

1. RestTemplate

1. 의존성 추가

order 서비스의 pom.xml

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  <version>2.2.8.RELEASE</version>
</dependency> 

2.2.8.RELEASE 버전을 사용했습니다

2. 부트스트랩 클래스에 @EnableCircuitBreaker 어노테이션 추가

EcommerceOrderApplication.java

@SpringBootApplication
@EnableCircuitBreaker
public class EcommerceOrderApplication {

	public static void main(String[] arg) {
    	SpringApplication.run(EcommerceOrderApplication.class, args);
    }
}

3. 서비스 메서드에 @HystrixCommand 추가하고 fallbackMethod 설정

OrderServiceImpl.java

@Override
@HystrixCommand(fallbackMethod = "insertOrderFallback")
public int insertOrder(Order order) throws Exception {
	Stirng url = customer_api_url + "/rest/customers/" + order.getUserId();
    Customer cust = restTemplate.getForObject(url, Customer.class);
	return orderRepository.insertOrder(order);
}

public int insertOrderFallback(Order order) throws Exception {
	// Hystrix 작동하면 fallback으로 동작할 부분
    ...
}
  • 이때, return type과 parameter type이 같아야 한다.

2. FeignClient

1. 의존성 추가

order 서비스의 pom.xml

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
  <version>2.2.8.RELEASE</version>
</dependency> 

2. application.properties에서 circuit breaker 활성화

application.properties

# feign client용 circuit breaker 활성화
feign.hystrix.enabled=true

3. 부트스트랩 클래스에 @EnableCircuitBreaker 어노테이션 추가

EcommerceOrderApplication.java

@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class EcommerceOrderApplication {

	public static void main(String[] arg) {
    	SpringApplication.run(EcommerceOrderApplication.class, args);
    }
}

4. FeignClient 인터페이스 FeignClient에 fallbackFactory 속성 추가

CustomerFeignClient.java

@FeignClient(name = "ecommerce-customer",
			url = "http://localhost:{customer 포트번호}/ecommerce/customer",
            fallbackFactory = CustomerFeignClientFallbackFactory.class)	
public interface CustomerFeignClient {
	
    @GetMapping(value="/rest/customers/{userid}")
    public Customer retrieveCustomer(@PathVariable("userid" String userid) throws Exception;
}

// FallbackFactory 인터페이스의 구현체를 정의하여 빈으로 등록
@Component
class CustomerFeignClientFallbackFactory implements FallbackFactory<CustomerFeignClient>{
	
    @Override
    public CustomerFeignClient create(Throwable cause) {
    	// 익명 클래스
        CustomerFeignClient client = new CustomerFeignClient() {
        	// Hystrix 작동하면 fallback으로 동작할 부분
        	...
        }
    	return client;
    }
}
  • @FeignClient에 fallbackFactory 속성을 추가하고 fallback 메서드로 사용할 Class를 구현
  • FallbackFactory의 create 메서드를 오버라이드

Hystrix 세부 설정값

Hystrix의 동작을 사용자 정의하는 방법을 알아보겠습니다.

Hystrix의 기본 설정은 다음과 같습니다.
hystrix.command.default.{property}
commandKey를 사용하여 Hystrix 별로 설정을 다르게 하는 경우는 다음과 같이 설정합니다.
hystrix.command.{commandKey}.{property}

excution.isolation.thread.timeoutInMilliseconds (기본값 1초, 1000)
-> Hystrix가 적용된 메서드의 타임아웃을 지정합니다. 설정된 시간 안에 메서드가 완료되지 못하면 Fallback 메서드가 호출됩니다.
metrics.rollingStats.timeInMilliseconds (기본값 10초, 10000)
-> 서킷 브레이커가 열리기 위한 조건을 체크할 시간. 아래 조건들과 함께 조건을 지정합니다.
circuitBreaker.errorThresholdPercentage (기본값 50)
-> 서킷 브레이커가 발동할 에러 퍼센트 지정.
circuitBreaker.requestVolumeThreshold (기본값 20)
-> 서킷 브레이커가 열리기 위한 최소 요청 조건. 이 값이 20이라면 10초간 19개의 요청 들어와 19개 전부 실패해도 서킷브레이커는 열리지 않습니다.
circuitBreaker.sleepWindowInMilliseconds (기본값 5초, 5000)
-> 서킷 브레이커가 열렸을 때 얼마나 지속될지를 설정합니다.

@HystrixCommand를 사용한 경우 commandKey 설정은 다음과 같이 합니다.

@HystrixCommand(fallbackMethod = "insertOrderFallback", commandKey="retrieveCustomer")
public int insertOrder(Order order) throws Exception {
	...
}

Feign Client는 Command Key가 {fegin client}#{method(param)} 의 형태로 만들어집니다. 예시) CustomerFeignClient#retrieveCustomer(String)


retrieveCustomer에 대해서만 임계값을 달리 설정하고 싶다면, 다음과 같이 설정합니다.

application.properties

# commandKey가 retrieveCustomer인 Hystrix에 대해서만 임계값 2000으로 변경
# 따로 설정하지 않은 것들은 default 값으로 적용
hystrix.command.retrieveCustomer.execution.isolation.thread.timeoutInMilliseconds = 2000

0개의 댓글