浑身尖刺的服务可用性守护者——hystrix熔断器实践记录

netflix公司的产品hystrix(长满刺的豪猪),在高可用目标下具有一定熔断、限流、降级的作用。
这里主要写一些自己在使用时的问题解决思路,原理请自行理解,包括线程池与信号量模式等。

注意三个参数的默认值:

1、commandGroup  默认为getClass().getSimpleName();

2、commandKey  默认为getClass().getSimpleName()(继承HystrixCommand方式)/ 方法名(注解方式),可与commandGroup相同;

3、threadPoolKey  默认与commandGroup相同,即一个group共用线程池。这个key也可以单独定义,原因是属于相同逻辑功能的“组”,其中每个命令占用的资源彼此之间可能需要隔离。


hystrix在普通java项目中有两种应用方式,首先引入依赖
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>1.5.12</version>
</dependency>
一、写一个类继承HystrixCommand,组合需要熔断的方法所在的接口或实现类,重写run()和getFallback(),
run()中写业务调用原方法的逻辑,getFallback()中可以通过getFailedExecutionException()处理方法执行异常。
在构造函数中通过super(Setter.xxx)配置参数。

附:设置参数示例
super(Setter
.withGroupKey(
HystrixCommandGroupKey.Factory.asKey(xxxWithCircuitBreaker.class.getName()))
.andThreadPoolKey(
HystrixThreadPoolKey.Factory.asKey(xxxWithCircuitBreaker.class.getName()))
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(5))// 服务线程池数量
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutEnabled(true)
.withExecutionTimeoutInMilliseconds(1000)// 超时时间
.withCircuitBreakerRequestVolumeThreshold(5)// 设置在一个滚动窗口中,打开断路器的最少请求数
.withCircuitBreakerErrorThresholdPercentage(60)// 熔断器关闭到打开的百分比阈值
.withCircuitBreakerSleepWindowInMilliseconds(5000)// 熔断器打开到关闭的时间窗长度
));

外部使用hystrixCommand.execute();调用。
注意:getFailedExecutionException()不包括超出withExecutionTimeoutInMilliseconds设置的时间时抛出的异常以及在熔断状态下调用方法时抛出的异常。
通过阅读源码发现HystrixCommand这个方式可以做更多扩展,比如通过getExecutionException()方法获取方法执行中的所有异常,这里是自己在测试过程中简单的异常处理逻辑:
Exception e = (Exception) getExecutionException();
if (e instanceof HystrixTimeoutException) {
logger.error("方法执行超时");
} else if (e instanceof RuntimeException) {
// 大部分原因是发生熔断阻断了方法请求
logger.error(e.getMessage());
} else if (null != getFailedExecutionException())
logger.error("方法执行异常", getFailedExecutionException());

 

二、@HystrixCommand注解方式
要额外引入一个依赖。
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>1.5.12</version>
</dependency>

spring配置文件中加入aop:

xmlns:aop="http://www.springframework.org/schema/aop"

。。。

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd

。。。

 

<aop:aspectj-autoproxy/>
<bean id="hystrixAspect"
class="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect"></bean>


hystrix使用Netflix Archaius作为配置中心(这个东西可以动态变更配置),默认配置文件在resources下,名字为config.properties。
如果需要指定配置文件位置,实践下来可以在项目启动过程中添加archaius所需的系统属性,如在spring监听器中添加。

例:// 通过archaius配置hystrix全局参数
System.setProperty("archaius.configurationSource.additionalUrls", "classpath:/properties/hystrix.properties");

当然hystrix配置有四个优先级,这里是全局配置,如果没记错的话,配置文件中针对某commandKey的配置 优先于 方法注解中的配置 优先于 配置文件中全局配置。

hystrix.properties示例内容(简单测试):
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=100  注:timeoutInMilliseconds单位ms,可以设置到1,不知实际是否能精确到1ms,简单测试没啥问题。
hystrix.command.default.circuitBreaker.requestVolumeThreshold=5
hystrix.command.default.execution.isolation.strategy=THREAD

这样在需要熔断的方法中只需指定相应fallback方法即可。
@HystrixCommand(fallbackMethod = "fallback1")
注解这里比继承HystrixCommand类的方式灵活的一点是可以在fallback方法中继续指定下一个fallback方法。
但不便的一点是只能从getFailedExecutionException()查看异常,其已经作为fallback方法的Throwable e参数了。

注意:

1、如果进入fallback,Hystrix会默认打印出getFailedExecutionException()的异常堆栈信息,比如超时就不会打印。

2、原则上fallback不允许抛出异常,但注解方式可以做到。Hystrix会打印如下两行加以提示。

13:40:34.035 [hystrix-BulkIndexServiceImpl-1] ERROR c.n.h.contrib.javanica.command.GenericCommand - failed to process fallback is the method: 'fallback1'.
13:40:34.038 [hystrix-BulkIndexServiceImpl-1] DEBUG com.netflix.hystrix.AbstractCommand - HystrixCommand execution COMMAND_EXCEPTION and fallback failed.

3、注解可以通过定义参数成员ignoreExceptions = {CustomException.class}来定义无需进入fallback方法的异常(直接抛出,Hystrix算作成功执行,不会触发熔断),但是如果超时还是会进fallback。

*4、个别情况下会发生run()方法和fallback()方法都成功执行完成,比如在测试时使用Thread.sleep() catch了InterruptedException,run()并未因超时而成功打断,在业务代码中应当注意try-catch问题,如果简单的catch Exception而catch之后仍有代码,则这些代码即时超时也会被执行。

 

附:注解配置示例
@HystrixCommand(groupKey = "hello", commandKey = "hello-service", threadPoolKey = "hello-pool", threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "5") }, commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")
}, fallbackMethod = "fallback1")

下篇mycat,欢迎交流指教。

posted on 2018-12-24 17:36  kurama2018  阅读(825)  评论(0编辑  收藏  举报