본문 바로가기

MSA/Spring

[Spring Cloud] Hystrix를 사용하여 Resilience한 시스템 구성하기

지난번 구성했던 POST 예제에 Hystrix를 적용하여 Resilience한 시스템을 구성해 봄

 

1. Hystrix?

MSA에서 분산된 서비스간 통신이 원할하지 않을 경우에 대비해 Netflix가 만든 Fault Tolerance Library

 

2. Background

Monolithic한 구조에서는 모듈사이의 메서드 호출이 실패하는 것을 고려하지 않음 ->시스템 자체가 죽지 않는 이상 그럴일이 없음

MSA에서 서로다른 서비스의 API 호출 시, 다양한 이유로 실패할 수 있는 가능성이 있음

API서버가 죽으면 Client 서버 역시 해당 서비스에 대해 장애가 생김 -> 장애가 전파됨

장애 전파를 막기 위해 API 호출 실패 시 대안을 적용해주어야 함

 

Hystrix를 활용하여 위 문제를 해결할 수 있다.


3. 예제

  3-1. Dependency

<!--TIP
	parent는 하나만 쓸 수 있다. 
	그렇기 때문에 groupId가 다른 cloud는 dependencyManagement로 의존성을 주입해주어야 한다.
	reference : https://spring.io/projects/spring-cloud 
-->
<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>${spring-cloud.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

  3-2. Ribbon Client(WebMain)

@EnableHystrix // Hystrix를 명시적으로 사용하기 위한 annotation 
@SpringBootApplication
public class WebMainApplication {

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

}
/*
 * Hystrix는 기본적으로 @Component / @Service를 찾고 
 * 그 안에 있는 @HystrixCommand를 찾아 동작한다. 
 */
@Service("empInfoService")
public class EmpInfoService {
    private static final Logger logger = LoggerFactory.getLogger(EmpInfoService.class);

    private WebClient webClient = WebClient.builder()
                                        .baseUrl("http://localhost:7077")
                                        .build();
    
    /* annotaion을 통해 CircuitBreaker 기능 활성화
     * 만약 Circuit이 Open이 될 경우 fallback method 수행
     * default timeout : 1 sec
     * HystrixProperty : https://github.com/Netflix/Hystrix/wiki/Configuration
     */
    @HystrixCommand(commandKey = "info", fallbackMethod = "getEmpInfoFallback",
                    commandProperties = {
                        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),    // set timeout value 3 sec
                        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "10"),       // error 비율이 10% 이상 발생하면 Circuit open
                        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2")           // 2회 이상 호출되면 통계 시작
                    })
    public EmpInfo getEmpInfo() {
        EmpInfo bodyEmpInfo = new EmpInfo();
        bodyEmpInfo.setId(777);
        bodyEmpInfo.setDomain("Jackpot");

        return webClient.post()         // POST method
            .uri("/api/getUserInfo")    // baseUrl 이후 uri
            .bodyValue(bodyEmpInfo)     // set body value
            .retrieve()                 // client message 전송
            .bodyToMono(EmpInfo.class)  // body type : EmpInfo
            .block();                   // await
    }

    // fallback method, Communication 실패시 수행되는 method
    private EmpInfo getEmpInfoFallback(Throwable t) {
        logger.info(t.getMessage());

        EmpInfo tmp = new EmpInfo();
        tmp.setId(0);
        tmp.setDomain("fallback123");

        return tmp;
    }
}

Reference

1. 기본기를 쌓는 정아마추어 코딩블로그Springboot hystrix 사용기 (hystrix로 마이크로 서비스 간의 서비스 호출 실패를 방지해보자), 정아마추어, 2019. 4. 16, https://jeong-pro.tistory.com/183 

2. 11번가 Spring Cloud 기반 MSA로의 전환, Spring Camp, 윤용성, 2018. 5. 27, https://www.slideshare.net/balladofgale/spring-camp-2018-11-spring-cloud-msa-1