나의 개발 일상 기록
[MSA] Hystrix Monitoring With Turbine 본문
Hystrix Monitoring With Turbine

Hystrix를 간략히 설명하자면 관련 서비스에 대한 호출 실패에 대한 method를 감시하고 있습니다. 실패가 있는 경우 Circuit Open하여 Fallback을 호출하여 전달해줍니다. 각 method에 @HystrixCommand를 달아 놓고 잘 동작하는지를 Monitoring 할 수 있는 Dashboard를 Hystrix에서 제공을 해줍니다.
Hystrix Dashboard는 single instance에 대해 @HystrixCommand가 정의된 method에 대한 Monitoring을 수행할수도 있게 해주고 Turbine을 이용해 multi instance에 대해 @HystrixCommand가 정의된 method에 대한 모니터링을 수행할 수있게도 해줍니다.
1. Supplier
Hystrix 시나리오를 만들려면 먼저 서비스가 필요합니다. 서비스를 제공할 Supplier 프로젝트를 생성합니다. Hystrix는 Client에만 설정해서 처리하므로 외부 API를 호출하는 경우에도 적용할 수 있습니다. 그러므로 Supplier에는 Hystrix 코드가 전혀 존재할 필요가 없으며 단순하게 구성을 하여도 상관없이 시나리오대로 흘러갈 수 있습니다.
로컬에서 충돌을 방지하기 위해 각 생성한 프로젝트 별로 서버 포트를 지정을 해줄겁니다. application 파일은 .yml or .properties 편한 것을 사용해도 상관이 없습니다.
- application.properties -
server.port=8090
서버 포트를 지정 하였으므로 단순한 REST API 하나만을 노출하는 SupplierApplication.java에 작성하였습니다.
- SupplierApplication.java -
@SpringBootApplication
public class SupplierApplication {
public static void main(String[] args) {
SpringApplication.run(SupplierApplication.class, args);
}
@RestController
static class supplierContoller{
@GetMapping("/supplier")
public String supplier() {
return "Hello Supplier";
}
}
}
서버를 실행하여 CMD에서 curl를 이용하여 supplier 호출을 하면 정상적으로 호출이 되는것을 확인할 수 있습니다.

2. Consumer (소비자)
Supplier에 API를 호출하는 Consumer가 필요합니다. spring-cloud -starter- hystrix, spring-boot-starter-web을 종속성으로 사용하여 새로운 Maven 프로젝트를 생성합니다.
생성 후 pom.xml에 spring-cloud -starter- hystrix를 의존성 주입을 해줍니다.
- pom.xml -
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
그 다음은 Consumer의 서버 포트를 지정해줍니다.
- application.properties -
server.port=9000
설정을 끝났으니 Consumer에 로직을 작성합니다.
- ConsumerApplication.java -
@EnableCircuitBreaker
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@RestController
static class consumerController{
@GetMapping("/consumer")
@HystrixCommand(fallbackMethod = "fallback")
public String consumer(@RequestParam String path) {
ResponseEntity<String> entity = new RestTemplate().getForEntity("http://127.0.0.1:8090/" + path, String.class);
if(entity.getStatusCode() == HttpStatus.OK) {
return entity.getBody();
}
throw new RuntimeException("supplier is not ok");
}
private String fallback(String path) {
return "Hello Fallback";
}
}
}
@EnableCircuitBreaker 어노테이션을 명시하여 CircuitBreaker를 활성화 시키고 Hystrix를 적용할 method에 @HystrixCommand 어노테이션을 사용하여 Hystrix 적용해줍니다. @HystrixCommand 애노테이션에 fallbackMethod를 설정 할 수 있습니다. fallbackMethod 설정 시 method가 에러가 발생하였을 경우 fallbackMethod에 설정한 method를 찾아가 해당 method를 호출하여 처리하게 됩니다.
Supplier 서버를 호출하여 HTTP상태코드가 OK(200)인 경우를 제외하고는 예외를 발생하게 하였습니다.
curl를 통해 서버 호출에 성공하였을 경우와 실패하였을 경우를 살펴보았습니다.

위의 그림에서 비정상적인 호출 건에 대해서는 fallback method가 호출이 되어 "Hello Fallback"이 나오는 것을 확인할 수가 있습니다. 정상적인 호출 건에 대해서는 "Hello Supplier"이 정상적으로 호출되는 것을 확인할 수가 있습니다.
* Consumer - HystrixProperty
@HystrixCommand 어노테이션에는 다양한 HystrixProperty 설정을 할 수 있다고 합니다. 자주 사용 한다는 설정에 대해서 잠깐 알아보았습니다.
@HystrixCommand(fallbackMethod = "fallback", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "10"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000")
}, threadPoolProperties = @HystrixProperty(name = "coreSize", value = "100"))
- execution.isolation.thread.timeoutInMilliseconds
Hystrix 가 적용된 method의 Timeout을 지정합니다. 이 Timeout 내에 method가 완료되지못하면 CircuitBreaker가 닫혀있다고 하더라도 fallback method가 호출됩니다. 보통 외부 API 를 호출하게되면 RestTemplate과 같은 http client에도 connect, read timeout 등을 지정하게하는데 hystrix timeout은 이를 포함하고 여유를 좀 더 두어 잡습니다. 기본값은 1초(1000)입니다.
- metrics.rollingStats.timeInMilliseconds
CircuitBreaker가 열리기위한 조건을 체크할 시간입니다. 아래에서 살펴볼 몇가지 조건들과 함께 조건을 정의하게되는데 "10초간 50% 실패하면 서킷 브레이커 발동" 이라는 조건이 정의되어있다면 여기서 10초를 맡습니다. 기본값은 10초(10000)입니다.
- circuitBreaker.errorThresholdPercentage
CircuitBreaker가 발동할 에러 퍼센트를 지정합니다. 기본값은 50입니다.
- circuitBreaker.requestVolumeThreshold
CircuitBreaker가 열리기 위한 최소 요청조건입니다. 즉 이 값이 20으로 설정되어있다면 10초간 19개의 요청이 들어와서 19개가 전부 실패하더라도 서킷 브레이커는 열리지않습니다. 기본값은 20입니다.
- circuitBreaker.sleepWindowInMilliseconds
CircuitBreaker가 열렸을때 얼마나 지속될지를 설정합니다. 기본값은 5초(5000)입니다.
- coreSize
위에서 별도의 설명은 안했는데 Hystrix 작동방식은 Thread를 이용하는 Thread 방식과 Semaphore 방식이 있습니다. Thread 를 이용할 경우 core size를 지정하는 속성이다. 넷플릭스에서는 공식 가이드에 왠만하면 Thread 방식을 권장하고있습니다.(디폴트 설정도 Thread 방식이다.) 기본 coreSize는 10입니다.
다양한 설정은 https://github.com/Netflix/Hystrix/wiki/configuration 에서 확인하시면 좋을것같습니다.
3. Monitoring
Hystrix를 적용 후 내가 설정한 내용들이 잘 적용이 되는 부분을 넷플릭스에서 제공하는 모니터링 방법으로 확인을 하기 위해 새로운 프로젝트를 생성합니다.
생성한 프로젝트에 spring-cloud-starter-netflix-hystrix-dashboard을 의존성 주입을 해줍니다.
- pom.xml -
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
pom.xml 의존성 주입 후 서버 포트를 지정해줍니다.
- application.properties -
server.port=9010
MonitorApplication에 @EnableHystrixDashboard 해당 어노테이션을 설정합니다.
- MonitorApplication.java -
@EnableHystrixDashboard
@SpringBootApplication
public class MonitorApplication {
public static void main(String[] args) {
SpringApplication.run(MonitorApplication.class, args);
}
}
해당 서버를 실행 후 http://localhost:9010/hystrix으로 접속을 하면 Hystrix 애플리케이션을 모니터링하는 화면이 보여주기 됩니다.

모니터링 화면에 Consumer에 대한 정보를 노출할 수 있도록하여 시각정보를 확인해보는 작업이 필요합니다.
2. Consumer에서 생성한 프로젝트에 pom.xml과 application.properties 파일에 필요한 정보를 추가하여야 합니다.
먼저 pom.xml에 spring-boot-starter-actuator을 의존성 주입을 합니다.
- pom.xml(Consumer) -
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.properties에 HTTP를 통해 노출 하려는 모든 endpoints를 열어주었습니다.
- application.properties(Consumer) -
management.endpoints.web.exposure.include=*
그럼이제 Supplier, Consumer, Monitor 서버를 실행하여 dashboard에 http://localhost:9000/actuator/hystrix.stream 입력합니다. 그럼 Circuit, Thread Pools 정보를 확인할수 있는 화면이 노출됩니다. 아직은 Consumer이 아무런 동작을 하지 않아기에 어떤한 정보도 노출이 되지않습니다.

curl를 통해 호출을 하면 아래의 그림처럼 Circuit, Thread Pools 정보가 시각화 자료로 노출이 됩니다.

* Circuit 시각화 정보에서 색상으로 표시된 정보의 의미는 아래의 그림을 참고하시면 됩니다.

4. Eureka
하나의 인스턴스 정보가 아닌 여러 개의 인트턴스 정보를 확인하기 위해서 인스턴스들을 집계할 Eureka와 Eureka 클라이언트들의 Hystrix 모니터링을 집계할 Turbine이 필요합니다. 우선 Eureka 프로젝트를 생성해 보겠습니다.
생성한 프로젝트의 pom.xml에 spring-cloud-starter-netflix-eureka-server 의존성 주입을 해줍니다.
- pom.xml -
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
의존성 주입 후 application.properties 파일에서 필요한 부분을 설정해줍니다.
- application.properties -
server.port=9020
spring.application.name=eureka
eureka.client.service-url.defaultZone=http://127.0.0.1:9020/eureka
eureka.client.fetch-registry: false
eureka.client.register-with-eureka: false
- server.port : 서버 포트를 지정합니다.
- spring.application.name : 어플리케이션 이름을 지정합니다.
- eureka.client.service-url.defaultZone : Eureka Client가 서비스를 등록할 서버 주소를 저장하기 위해 해당 속성을 사용합니다.
- eureka.client.fetch-registry : 이 속성을 true로 구성하면 내장 클라이언트가 Eureka 레지스트리를 가져오려고 합니다.
- eureka.client.register-with-eureka : 이 속성을 true로 설정하면 서버가 시작되는 동안 내장 클라이언트가 Eureka 서버에 자신을 등록하려고 시도합니다.
* eureka.client.fetch-registry / eureka.client.register-with-eureka 속성을 false로 해주는 이유
Eureka서버를 시작할 때 내장 클라이언트를 등록하여 서버에 자체 구성하는 것을 원하지 않습니다. 서버를 시작하는 동안 2개의 속성을 true로 표시하거나 기본적으로 true로 구성하지 않으면 내장 클라이언트가 Eureka서버에 자신을 등록 하려고 시도하고 아직 사용할 수 없는 레지스트리도 가져오려고 시도합니다. 결과적으로 TransportException이 발생합니다.
EurekaApplication에 @EnableEurekaServer 어노테이션을 추가합니다.
- EurekaApplication.java -
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
Eureka 프로젝트는 작성이 완료 되었습니다. Eureka 서버를 실행을 시도 후 java.lang.TypeNotPresentException: Type javax.xml.bind.JAXBContext not present에러가 발생한다면 pom.xml에 다음과 같이 추가를 해줍니다.
- pom.xml -
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
Eureka 서버를 실행시켜 http://localhost:9020/ 으로 접속 시 Eureka Client가 보여집니다.

Eureka 프로젝트를 완성하였으니 Consumer에 Eureka등록을 하여 Eureka Client에 Consumer가 노출이 되는지 확인을 해봅니다.
Consumer의 pom.xml 파일에 spring-cloud-starter-netflix-eureka-client을 의존성 주입을 합니다.
- pom.xml(Consumer) -
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
의존성 주입 후 application.properties 파일에 application name와 defaultZone 설정을 해줍니다.
- application.properties(Consumer) -
spring.application.name=consumer
eureka.client.service-url.defaultZone=http://127.0.0.1:9020/eureka
ConsumerApplication.java에 @EnableDiscoveryClient 어노테이션을 추가합니다.
- ConsumerApplication.java -
@EnableDiscoveryClient
@EnableCircuitBreaker
@SpringBootApplication
다시 서버를 실행하여 Consumer가 노출되는지 확인합니다.

5. Turbine
이제는 Eureka 클라이언트들의 Hystrix 모니터링을 집계할 Turbine 프로젝트를 생성합니다. 생성한 프로젝트의 pom.xml에 spring-cloud-starter-netflix-turbine 의존성 주입을 해줍니다.
- pom.xml -
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
의존성 주입 후 application.properties파일에 필요한 설정을 합니다.
- application.properties -
server.port:9030
spring.application.name:turbine
eureka.client.service-url.defaultZone:http://127.0.0.1:9020/eureka/
turbine.cluster-name-expression:new String('default')
turbine.app-config:consumer
- turbine.cluster-name-expression : 클러스터 이름을 표현 방식 지정
- turbine.app-config : 통합 모니터링을 할 어플리케이션의 spring.application.name에 대한 정보, 어플리케이션 네임의 갯수만큼 모니터링을 확인할 수 있습니다.
TurbineApplication.java에서 @EnableDiscoveryClient, @EnableTurbine 어노테이션을 추가합니다.
- TurbineApplication.java -
@EnableDiscoveryClient
@EnableTurbine
@SpringBootApplication
public class TurbineApplication {
public static void main(String[] args) {
SpringApplication.run(TurbineApplication.class, args);
}
}
Consumer, Eureka, Turbine 서버를 실행 후 http://localhost:9020/으로 접속을 하면 Turbine이 추가가 된것을 확인할 수있습니다.

그리고 모든 서버 실행 후 Hystrix Dashboard에서 모니터링할 곳은 아까와 달리 localhost:9030/turbine.stream을 적어줍니다.

현재 하나의 인스턴스밖에 나오지 않은 이유는 application.properties에서 turbine.app-config에 하나만 기입했기 때문입니다. 만약 2개이상의 서비스를 기입했다면 아래의 그림처럼 그 갯수만큼 표시가 됩니다.

'MSA' 카테고리의 다른 글
| [MSA] MSA 운영 환경을 위한 전용 모니터링 도구 (0) | 2021.07.21 |
|---|---|
| [MSA] Hystrix, Ribbon, Eureka in Spring Cloud Zuul (0) | 2021.07.20 |
| [MSA] Spring Cloud Netflix (0) | 2021.07.14 |
| [MSA] MSA(Microservices Architecture) (0) | 2021.07.12 |