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): 회로 차단기는 요청 자원이 온라인 상태인지 주기적으로 확인하고, 사람의 개입 없이 자원 접근을 다시 허용할 수 있습니다.
- 폴백 패턴을 사용하면 원격 서비스에 대한 호출이 실패할 때 예외(exception)를 발생시키지 않고 서비스 소비자가 대체 코드 경로를 실행해 다른 방법으로 작업을 수행할 수 있습니다.
- 일반적으로 이 패턴은 다른 데이터 소스에서 데이터를 찾거나 향후 처리를 위해 사용자 요청을 큐(queue)에 입력하는 작업과 연관됩니다.
- Netflix가 제공하는 컴포넌트로, Circuit breaker 패턴을 자바 기반으로 오픈소스화한 라이브러리입니다.
- 서비스가 장애 내성, 지연 내성을 갖도록 도와줄 뿐만 아니라 모니터링 기능도 제공합니다.
그렇다면 직접 Hystrix를 사용하여 Circuit Breaker Pattern을 적용해 보고, 폴백 전략까지 구현해 봅시다~
ecommerce 프로젝트의 order 서비스에서 customer 서비스 컴포넌트로 사용자의 이름을 요청하는 경우를 예제로 설정합니다.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
2.2.8.RELEASE 버전을 사용했습니다
@SpringBootApplication
@EnableCircuitBreaker
public class EcommerceOrderApplication {
public static void main(String[] arg) {
SpringApplication.run(EcommerceOrderApplication.class, args);
}
}
@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으로 동작할 부분
...
}
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
# feign client용 circuit breaker 활성화
feign.hystrix.enabled=true
@SpringBootApplication
@EnableFeignClients
@EnableCircuitBreaker
public class EcommerceOrderApplication {
public static void main(String[] arg) {
SpringApplication.run(EcommerceOrderApplication.class, args);
}
}
@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;
}
}
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)
# commandKey가 retrieveCustomer인 Hystrix에 대해서만 임계값 2000으로 변경
# 따로 설정하지 않은 것들은 default 값으로 적용
hystrix.command.retrieveCustomer.execution.isolation.thread.timeoutInMilliseconds = 2000