...
Code Block |
---|
@EnableCircuitBreaker |
[display]
...
ProductRemoteServiceImpl에 @HystrixCommand 추가
Code Block |
---|
@HystrixCommand |
...
- [product] ProductController에서 항상 Exception 던지게 수정하기 (장애 상황 흉내)
Code Block @RestController @RequestMapping("/products") public class ProductController { @GetMapping(path = "{productId}") public String getProductInfo(@PathVariable String productId) { // return "[product id = " + productId + " at " + System.currentTimeMillis() + "]"; throw new RuntimeException("I/O Exception"); } }
- [display] ProductRemoteServiceImp에 ProductRemoteServiceImpl에 Fallback Method 작성하기
Code Block @Service public class ProductRemoteServiceImpl implements ProductRemoteService { private static final String url = "http://localhost:8082/products/"; private final RestTemplate restTemplate; public ProductRemoteServiceImpl(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @Override @HystrixCommand(fallbackMethod = "getProductInfoFallback") public String getProductInfo(String productId) { return this.restTemplate.getForObject(url + productId, String.class); } public String getProductInfoFallback(String productId) { return "[ this product is sold out ]"; } }
...
03. Hystrix로 Timeout 처리하기
Code Block |
---|
git checkout tags/step-2-hystrix-timeout -b step-2-hystrix-timeout |
- [product] ProductController의 throw Exception을 Thread.sleep(2000)로 수정
Code Block @RestController @RequestMapping("/products") public class ProductController { @GetMapping(path = "{productId}") public String getProductInfo(@PathVariable String productId) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "[product id = " + productId + " at " + System.currentTimeMillis() + "]"; //throw new RuntimeException("I/O Exception"); } }
- [display] application.yml 수정하여 Hystrix Timeout 시간 조정하기
Code Block hystrix: command: default: # command key. use 'default' for global setting. execution: isolation: thread: timeoutInMilliseconds: 1000
- Display → Product 호출 하기
Info icon false Code Block 2020-02-25 12:12:54.768 INFO 92144 --- [io-8081-exec-10] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet' 2020-02-25 12:12:54.769 INFO 92144 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started 2020-02-25 12:12:54.857 INFO 92144 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 88 ms t = com.netflix.hystrix.exception.HystrixTimeoutException
- 정리
Info icon false - Hystrix를 통해 실행 되는 모든 메소드는 정해진 응답시간 내에 반환 되어야 한다.
- 그렇지 못한 경우, Exception이 발생하며, Fallback이 정의된 경우 수행된다.
- Timeout 시간은 조절할 수 있다. (Circuit 별로 세부 수정 가능하며 뒷 부분에 설명)
- 언제 유용한가 ?
- 항상 !!
- 모든 외부 연동은 최대 응답 시간을 가정할 수 있어야 한다.
- 여러 연동을 사용하는 경우 최대 응답시간을 직접 Control하는 것은 불가능하다 (다양한 timeout, 다양한 지연등..)
04. Hystrix Circuit Open 테스트
Info |
---|
git checkout tags/step-2-hystrix-circuit-open -b step-2-hystrix-circuit-open |
- [display] application.yml 수정하여 Hystrix 프로퍼티 추가.
- 10초동안 20개 이상의 호출이 발생 했을때 50% 이상의 호출에서 에러가 발생하면 Circuit Open
Code Block hystrix: command: default: # command key. use 'default' for global setting. execution: isolation: thread: timeoutInMilliseconds: 3000 circuitBreaker: requestVolumeThreshold: 1 # Minimum number of request to calculate circuit breaker's health. default 20 errorThresholdPercentage: 50 # Error percentage to open circuit. default 50
- [product] ProductController 다시 수정하여 Exception 던지도록 수정
Code Block @RestController @RequestMapping("/products") public class ProductController { @GetMapping(path = "{productId}") public String getProductInfo(@PathVariable String productId) { // try { // Thread.sleep(2000); // } catch (InterruptedException e) { // e.printStackTrace(); // } // // return "[product id = " + productId + " at " + System.currentTimeMillis() + "]"; throw new RuntimeException("I/O Exception"); } }
- Display → Product 호출 확인
Info Code Block 2020-02-25 12:38:13.284 INFO 96380 --- [io-8081-exec-10] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet' 2020-02-25 12:38:13.285 INFO 96380 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started 2020-02-25 12:38:13.360 INFO 96380 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 74 ms t = org.springframework.web.client.HttpServerErrorException: 500 null t = java.lang.RuntimeException: Hystrix circuit short-circuited and is OPEN t = java.lang.RuntimeException: Hystrix circuit short-circuited and is OPEN t = java.lang.RuntimeException: Hystrix circuit short-circuited and is OPEN
- 정리
Info icon false - Circuit Open 여부는 통계를 기반으로 한다.
- 최근 10초간 호출 통계
- metrics.rollingStats.timeInMilliseconds : 10000
- 최소 요청 갯수(20) 넘는 경우만
- circuitBreaker.requestVolumeThreshold : 20
- 에러 비율 넘는 경우(50%)
- circuitBreaker.errorThresholdPercentage : 50
- 한번 Circuit이 오픈되면 5초간 호출이 차단되며, 5초 경과후 단 “1개”의 호출을 허용하며 (Half-Open), 이것이 성공하면 Circuit을 다시 CLOSE하고, 여전히 실패하면 Open이 5초 연장된다.
- circuitBreaker.sleepWindowInMilliseconds : 5000
- Circuit Breaker의 단위 ?
- 에러 비율을 통계의 단위
- Circuit Open / Close가 함께 적용되는 단위
- 즉, A 메소드와 B 메소드가 같은 Circuit Breaker를 사용한다면, A와 B의 에러 비율이 함께 통계내어지고, Circuit이 오픈되면 함께 차단된다.
- Circuit의 지정은 ?
- 'commandKey' 라는 프로퍼티로 지정 가능.
- @HystrixCommand에서는 지정하지 않는 경우 메소드 이름 사용
- 이렇게 사용하지 말것 !
- 메소드 이름은 겹칠 수 있으며, 나중에 나오는 Feign의 경우 또 다르기 때문에 헷갈 릴 수 있다.
- 항상 직접 지정 해서 사용하기
05. Hystrix Circuit Open commandKey 부여하기
Code Block |
---|
git checkout tags/step-2-hystrix-command-key -b step-2-hystrix-command-key |
- [display] 'commandKey' 부여하기
Code Block @Service public class ProductRemoteServiceImpl implements ProductRemoteService { private static final String url = "http://localhost:8082/products/"; private final RestTemplate restTemplate; public ProductRemoteServiceImpl(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @Override @HystrixCommand(commandKey = "productInfo", fallbackMethod = "getProductInfoFallback") public String getProductInfo(String productId) { return this.restTemplate.getForObject(url + productId, String.class); } public String getProductInfoFallback(String productId, Throwable t) { System.out.println("t = " + t); return "[ this product is sold out ]"; } }
- [display] application.yml에 commandKey로 속성 지정해보기
Code Block hystrix: command: productInfo: # command key. use 'default' for global setting. execution: isolation: thread: timeoutInMilliseconds: 3000 circuitBreaker: requestVolumeThreshold: 1 # Minimum number of request to calculate circuit breaker's health. default 20 errorThresholdPercentage: 50 # Error percentage to open circuit. default 50