Hystrix Dashboard 需要输入单个服务的 hystrix.stream 监控端点,只能监控单个服务,当需要监控整个系统和集群的时候,这种方式就显得很鸡肋,此时可以使用 Turbine 来做监控。
Turbine 是为了聚合所有相关的 hystrix.stream 流的方案,然后在 hystrix dashboard 中展示。
Turbine
源码:https://gitee.com/laiyy0728/spring-cloud/tree/master/spring-cloud-hystrix/spring-cloud-hystrix-dashboard/spring-cloud-hystrix-dashboard-turbine
微服务继续沿用上例中的 hello-service、provider-service,新建 Turbine 项目
pom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-turbine</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies>
|
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| server: port: 9999
spring: application: name: spring-cloud-hystrix-dashboard-turbne
eureka: instance: prefer-ip-address: true instance-id: ${spring.application.name}:${server.port} client: service-url: defaultZone: http://localhost:8761/eureka/
management: endpoints: web: exposure: exclude: hystrix.stream turbine: app-config: spring-cloud-hystrix-dashboard-hello-service,spring-cloud-hystrix-dashboard-provider-service cluster-name-expression: "'default'"
|
启动类
1 2 3 4 5 6 7 8 9 10 11
| @SpringBootApplication @EnableDiscoveryClient @EnableHystrixDashboard @EnableTurbine public class SpringCloudHystrixDashboardTurbineApplication {
public static void main(String[] args) { SpringApplication.run(SpringCloudHystrixDashboardTurbineApplication.class, args); }
}
|
验证
浏览器中访问 Turbine: http://localhost:9999/hystrix ,输入 Turbine 监控端点:http://localhost:9999/turbine.stream
访问 hello-service(http://localhost:8080/get-provider-data)、provider-service(http://localhost:8081/get-hello-service),再次查看 turbine
异常处理
源码:https://gitee.com/laiyy0728/spring-cloud/tree/master/spring-cloud-hystrix/spring-cloud-hystrix-dashboard/spring-cloud-hystrix-dashboard-exception
Hystrix 的异常处理中,有五种出错情况会被 Fallback 截获,触发 Fallbac
- FAILURE:执行失败,抛出异常
- TIMEOUT:执行超时
- SHORT_CIRCUITED:断路器打开
- THREAD_POOL_REJECTED:线程池拒绝
- SEMAPHORE_REJECTED:信号量拒绝
但是有一种类型的异常不会触发 Fallback 且不会被计数、不会熔断——BAD_REQUEST
。BAD_ERQUEST 会抛出 HystrixBadRequestException,这种异常一般是因为对应的参数或系统参数异常引起的,对于这类异常,可以根据响应创建对应的异常进行异常封装或直接处理。
BAD_REQUEST 处理
pom
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
|
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| server: port: 9999
spring: application: name: spring-cloud-hystrix-dashboard-exception
eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true instance-id: ${spring.application.name}:${server.port}
management: endpoints: web: exposure: exclude: hystrix.stream
feign: hystrix: enabled: true
|
异常处理
在 Hystrix 中处理异常,需要继承 HystrixCommand 并重写 run、getFallback 方法
bad request
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class FallBackBadRequestException extends HystrixCommand<String> {
private static final Logger LOGGER = LoggerFactory.getLogger(FallBackBadRequestException.class);
public FallBackBadRequestException() { super(HystrixCommandGroupKey.Factory.asKey("GroupBadRequestException")); }
@Override protected String run() throws Exception { throw new HystrixBadRequestException("this is HystrixBadRequestException!"); }
@Override protected String getFallback() { System.out.println("Fallback 错误信息:" + getFailedExecutionException().getMessage()); return "this is HystrixBadRequestException Fallback method!"; } }
|
其他错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class FallBackOtherException extends HystrixCommand<String> {
public FallBackOtherException() { super(HystrixCommandGroupKey.Factory.asKey("otherException")); }
@Override protected String run() throws Exception { throw new Exception("other exception"); }
@Override protected String getFallback() { return "fallback!"; } }
|
模拟 feign 调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class ProviderServiceCommand extends HystrixCommand<String> {
private final String name;
public ProviderServiceCommand(String name){ super(HystrixCommandGroupKey.Factory.asKey("springCloud")); this.name = name; }
@Override protected String run() throws Exception { return "spring cloud!" + name; }
@Override protected String getFallback() { return "spring cloud fail!"; } }
|
Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @RestController public class ExceptionController {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionController.class);
@GetMapping(value = "provider-service-command") public String providerServiceCommand(){ return new ProviderServiceCommand("laiyy").execute(); }
@GetMapping(value = "fallback-bad-request") public String fallbackBadRequest(){ return new FallBackBadRequestException().execute(); }
@GetMapping(value = "fallback-other") public String fallbackOther(){ return new FallBackOtherException().execute(); }
@GetMapping(value = "fallback-method") @HystrixCommand(fallbackMethod = "fallback") public String fallbackMethod(String id){ throw new RuntimeException("fallback method !"); }
public String fallback(String id, Throwable throwable){ LOGGER.error(">>>>>>>>>>>>>>> 进入 @HystrixCommand fallback!"); return "this is @HystrixCommand fallback!"; } }
|
验证
依次请求 Controller 中的接口,可以看到,除了 fallback-bad-request
外,其他的接口都进入了 Fallback 方法中。证明了 BAD_REQUEST 不会触发 Fallback。
BAD_REQUEST Fallback
可以使用 ErrorDecoder 对 BAD_REQUEST 进行包装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Component public class BadRequestErrorDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { if (response.status() >= 400 && response.status() <= 499) { String error = null; try { error = Util.toString(response.body().asReader()); } catch (IOException e) { System.out.println("BadRequestErrorDecoder 出错了!" + e.getLocalizedMessage()); } return new HystrixBadRequestException(error); } return FeignException.errorStatus(methodKey, response); } }
|
之后在 yml 配置文件中增加对微服务调用的 ErrorDecoder 配置
1 2 3 4 5 6 7
| feign: hystrix: enabled: true client: config: spring-cloud-hystrix-dashboard-provider-service: errorDecoder: com.laiyy.gitee.dashboard.springcloudhystrixdashboardexception.decoder.BadRequestErrorDecoder
|
Hystrix 配置
一个简单的 Hystrix 配置,基本有一下几个内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| hystrix: command: default: circuitBreaker: errorThresholdPercentage: 50 forceOpen: false execution: isolation: strategy: THREAD thread: timeoutInMilliseconds: 5000 interruptOnTimeout: true semaphore: maxConcurrentRequests: 10 timeout: enabled: true threadpool: default: coreSize: 10 maximumSize: 10 allowMaximumSizeToDivergeFromCoreSize: false
|
隔离策略
1 2 3 4 5 6
| hystrix: command: default: execution: isolation: strategy: THREAD
|
隔离策略有两种:线程隔离策略和信号量隔离策略。分别对应:THREAD
、SEMAPHORE
。
线程隔离
Hystrix 默认的隔离策略,通过线程池大小可以控制并发量,当线程饱和时,可以拒绝服务,防止出现问题。
优点:
- 完全隔离第三方应用,请求线程可以快速收回
- 请求线程可以继续接受新的请求,如果出现线程问题,线程池隔离是独立的,不会影响其他应用
- 当失败的应用再次变得可用时,线程池将清理并可以立即恢复
- 独立的线程池提高了并发性
缺点:
- 增加CPU开销,每个命令的执行都涉及到线程的排队、调度、上下文切换等。
信号量隔离
使用一个原子计数器(信号量)来记录当前有多少线程正在运行,当请求进来时,先判断计数器的数值(默认10),如果超过设置则拒绝请求,否则正常执行,计数器+1。成功执行后,计数器-1。
与线程隔离
的最大区别是,执行请求的线程依然是请求线程
,而不是线程隔离中分配的线程池。
对单个 HystrixCommand 配置隔离策略等
1 2 3 4
| @HystrixCommand(fallbackMethod = "defaultUser", commandProperties = { @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "THREAD") })
|
应用场景
线程隔离:第三方应用、接口;并发量大
信号量隔离:内部应用、中间件(redis);并发量不大
HystrixCommandKey、HystrixThreadPoolKey
HystrixCommandKey 是一个 @HystrixCommand 注解标注的方法的 key,默认为标注方法的方法名
,也可以使用 @HystrixCommand 进行配置
HystrixThreadPoolKey 是 Hystrix 开启线程隔离策略后,指定的线程池名称,可以使用 @HystrixCommand 配置
1 2 3 4 5 6 7 8 9 10 11 12 13
| @HystrixCommand(fallbackMethod = "defaultUser", commandProperties = { @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "THREAD") }, commandKey = "commandKey", threadPoolKey = "threadPoolKey", threadPoolProperties = { @HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_THREAD_INTERRUPT_ON_TIMEOUT, value = "5000") })
|