Spring Cloud Hystrix 断路器
1.Hystrix 由来
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又在调用其他的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,那么对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,这就是所谓的“雪崩效应”。
那么为了解决“雪崩效应”Hystrix应运而生,Hystrix 是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多服务无法避免会调用失败,比如超时、异常等等,Hystrix能够保证在一个服务出现问题的情况下,不会导致整体服务的失败,避免级联故障,以提高分布式系统的弹性。
所以叫“断路器”。“断路器”是一种开关装置,就好比我们家里的熔断保险丝,当出现突发情况,会自动跳闸,避免整个电路烧坏。那么当某个服务发生故障时,通过 Hystrix,会向调用方返回一个符合预期的、可处理的默认响应(也称备选响应,即fallBack),而不是长时间的等待或者直接返回一个异常信息。这样就能保证服务调用方可以顺利的处理逻辑,而不是那种漫长的等待或者其他故障。
这就叫“服务熔断”,就跟熔断保险丝一个道理。
2.Hystrix 使用
上面介绍了 Hystrix 的基本原理,接下来我们来落地到代码实现
2.1依赖导入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2.2启动类注解
在启动类中,需要添加 @EnableCircuitBreaker 注解
@SpringBootApplication
@EnableEurekaClient
@ComponentScan({"com.joy.*","cn.com.joy.*"})
@EnableCircuitBreaker
public class O2OManagementApplication {
public static void main(String[] args) {
SpringApplication.run(O2OManagementApplication.class, args);
}
}
用@SpringCloudApplication最好,因为该注解包含@EnableCircuitBreaker
@SpringCloudApplication
@EnableFeignClients
@ComponentScan({"com.joy.*","cn.com.joy.*"})
public class O2OManagementApplication {
public static void main(String[] args) {
SpringApplication.run(O2OManagementApplication.class, args);
}
}
2.3对接口的改动
代码入下:
@RequestMapping("/view/createCase")
@HystrixCommand(fallbackMethod ="createCaseFallBack",ignoreExceptions = {NullPointerException.class, ArithmeticException.class},)
public String viewCreateStore(Model model) throws Exception
{
//这里默认查询 所有一级标签
LabelListFeign labelListFeign = new LabelListFeign();
labelListFeign.setDataType(1);
labelListFeign.setName("");
labelListFeign.setParentId(0);
List<Map<String,String>> labelInfoLevelOneList = labelManagerService.lable_lableNameList(labelListFeign);
model.addAttribute("labelInfoLevelOneList", labelInfoLevelOneList);
return "/case/case_create";
}
public String createCaseFallBack(Model model) {
model.addAttribute("message", "系统异常:NOTICE-SERVICE 服务调用异常!");
return "../../error/error";
}
我来分析一下代码:labelManagerService 是Feign 接口,我们使用 Feign 来接口式调用。我们看到,在 viewCreateStore(Model model) 方法上添加了 @HystrixCommand(fallbackMethod = “createCaseFallBack”) 注解,我简单解释一下:
@HystrixCommand 表示该接口开启 hystrix 熔断机制,如果出现问题,就去调用 fallbackMethod 属性指定的 createCaseFallBack 方法,那么往下看,就能看到 createCaseFallBack 方法,我们返回了和上面接口一样的数据结构,只不过都是我们自己搞的默认值而已。
viewCreateStore(Model model) 这个接口中,当labelManagerService调用lable_lableNameList方法异常时就会进入fallback方法中。
自行测试
3定义 Hystrix 处理类
上面介绍了 hystrix 的服务熔断和降级处理,但是有没有发现一个问题,这个 @HystrixCommand 注解是加在 Controller 层的接口方法上的,这会导致两个问题:
第一:如果接口方法很多,那么我是不是要在每个方法上都得加上该注解,而且,针对每个方法,我都要指定一个处理函数,这样会导致 Controller 变得越来越臃肿。
第二:这也不符合设计规范,理论上来说,Controller 层就是 Controller 层,我只管写接口即可。就像上一节介绍的 Feign,也是面向接口的,做均衡处理,我自己定义一个接口专门用来做均衡处理,在 Controller 层将该接口注入即可。那么 hystrix 是否也可以有类似的处理呢?
答案是有的。
3.1定义 Hystrix 处理类
下面是定义 Hystrix 处理类:
/**
* 统一处理熔断
* SearchServiceFeignService是Feign接口,所有访问都会走feign接口
* @author wangjun
*/
@Component
public class SearchClientServiceFallbackFactory implements FallbackFactory<SearchServiceFeignService>{
private static final Logger logger = LoggerFactory.getLogger(
SearchClientServiceFallbackFactory.class);
private static final String message = "调用search-service异常";
private static final String code = "600";
//组装返回的参数并写入日志
public ResponseBean fallbackfaction(Throwable cause) {
Map<String,Object> data = new HashMap<String, Object>();
data.put("code", code);
data.put("page", new HashMap<String, Object>());
data.put("message", message);
logger.error("fallback;reason was:",cause);
return ResponseBean.success(data);
}
@Override
public SearchServiceFeignService create(Throwable cause) {
return new SearchServiceFeignService() {
//后管店铺查询
@Override
public ResponseBean searchStoreList(SearchStoreListFeign searchStoreListFeign){
return fallbackfaction(cause);
}
...
};
}
}
我来分析一下代码,实现了 FallbackFactory 接口后,需要重写 create 方法,还是返回 OrderClientService 接口对象,只不过对这个 feign 客户端做了默认处理。
3.2给 Feign 指定 hystrix
OK,现在 hystrix 是绑定了 Feign 接口了,但是 Feign 接口中的某个方法如果出问题了,它怎么知道找谁去做熔断呢?所以在 Feign 接口也需要绑定一下我们定义的 hystrix 处理类:
@FeignClient(value = "SEARCH-SERVICE", fallbackFactory = SearchClientServiceFallbackFactory.class)
public interface SearchServiceFeignService {
/**
* 店铺列表(后管)
* @param searchStoreListFeign
* @return
* @throws Exception
*/ @RequestMapping(value="/o2o/searchStoreList",method=RequestMethod.POST)
public ResponseBean searchStoreList(@RequestBody SearchStoreListFeign searchStoreListFeign)
throws Exception;
...
}
添加了个 fallbackFactory 属性,指定了自定义的 hystrix 处理类。这样的话,Controller 中的所有方法都可以在 hystrix 里有个默认实现了。
还有要在application.properties开启熔断:
# 开启熔断
feign.hystrix.enabled=true
然后,就自行测试吧。
以上一个配置就够了,遇到项目需要的话。还有其他配置如下,按需配置即可
hystrix.command.default和hystrix.threadpool.default中的default为默认CommandKey
Command Properties
Execution相关的属性的配置:
hystrix.command.default.execution.isolation.strategy 隔离策略,默认是Thread, 可选Thread|Semaphore
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds 命令执行超时时间,默认1000ms
hystrix.command.default.execution.timeout.enabled 执行是否启用超时,默认启用true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout 发生超时是是否中断,默认true
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests 最大并发请求数,默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。如果达到最大并发请求数,请求会被拒绝。理论上选择semaphore size的原则和选择thread size一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread。
semaphore应该占整个容器(tomcat)的线程池的一小部分。
Fallback相关的属性
这些参数可以应用于Hystrix的THREAD和SEMAPHORE策略
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests 如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用。默认10
hystrix.command.default.fallback.enabled 当执行失败或者请求被拒绝,是否会尝试调用hystrixCommand.getFallback() 。默认true
Circuit Breaker相关的属性
hystrix.command.default.circuitBreaker.enabled 用来跟踪circuit的健康性,如果未达标则让request短路。默认true
hystrix.command.default.circuitBreaker.requestVolumeThreshold 一个rolling window内最小的请求数。如果设为20,那么当一个rolling window的时间内(比如说1个rolling window是10秒)收到19个请求,即使19个请求都失败,也不会触发circuit break。默认20
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds 触发短路的时间值,当该值设为5000时,则当触发circuit break后的5000毫秒内都会拒绝request,也就是5000毫秒后才会关闭circuit。默认5000
hystrix.command.default.circuitBreaker.errorThresholdPercentage错误比率阀值,如果错误率>=该值,circuit会被打开,并短路所有请求触发fallback。默认50
hystrix.command.default.circuitBreaker.forceOpen 强制打开熔断器,如果打开这个开关,那么拒绝所有request,默认false
hystrix.command.default.circuitBreaker.forceClosed 强制关闭熔断器 如果这个开关打开,circuit将一直关闭且忽略circuitBreaker.errorThresholdPercentage
Metrics相关参数
hystrix.command.default.metrics.rollingStats.timeInMilliseconds 设置统计的时间窗口值的,毫秒值,circuit break 的打开会根据1个rolling window的统计来计算。若rolling window被设为10000毫秒,则rolling window会被分成n个buckets,每个bucket包含success,failure,timeout,rejection的次数的统计信息。默认10000
hystrix.command.default.metrics.rollingStats.numBuckets 设置一个rolling window被划分的数量,若numBuckets=10,rolling window=10000,那么一个bucket的时间即1秒。必须符合rolling window % numberBuckets == 0。默认10
hystrix.command.default.metrics.rollingPercentile.enabled 执行时是否enable指标的计算和跟踪,默认true
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds 设置rolling percentile window的时间,默认60000
hystrix.command.default.metrics.rollingPercentile.numBuckets 设置rolling percentile window的numberBuckets。逻辑同上。默认6
hystrix.command.default.metrics.rollingPercentile.bucketSize 如果bucket size=100,window=10s,若这10s里有500次执行,只有最后100次执行会被统计到bucket里去。增加该值会增加内存开销以及排序的开销。默认100
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds 记录health 快照(用来统计成功和错误绿)的间隔,默认500ms
Request Context 相关参数
hystrix.command.default.requestCache.enabled 默认true,需要重载getCacheKey(),返回null时不缓存
hystrix.command.default.requestLog.enabled 记录日志到HystrixRequestLog,默认true
Collapser Properties 相关参数
hystrix.collapser.default.maxRequestsInBatch 单次批处理的最大请求数,达到该数量触发批处理,默认Integer.MAX_VALUE
hystrix.collapser.default.timerDelayInMilliseconds 触发批处理的延迟,也可以为创建批处理的时间+该值,默认10
hystrix.collapser.default.requestCache.enabled 是否对HystrixCollapser.execute() and HystrixCollapser.queue()的cache,默认true
ThreadPool 相关参数
线程数默认值10适用于大部分情况(有时可以设置得更小),如果需要设置得更大,那有个基本得公式可以follow:
requests per second at peak when healthy × 99th percentile latency in seconds + some breathing room
每秒最大支撑的请求数 (99%平均响应时间 + 缓存值)
比如:每秒能处理1000个请求,99%的请求响应时间是60ms,那么公式是:
(0.060+0.012)
基本得原则时保持线程池尽可能小,他主要是为了释放压力,防止资源被阻塞。
当一切都是正常的时候,线程池一般仅会有1到2个线程激活来提供服务
hystrix.threadpool.default.coreSize 并发执行的最大线程数,默认10
hystrix.threadpool.default.maxQueueSize BlockingQueue的最大队列数,当设为-1,会使用SynchronousQueue,值为正时使用LinkedBlcokingQueue。该设置只会在初始化时有效,之后不能修改threadpool的queue size,除非reinitialising thread executor。默认-1。
hystrix.threadpool.default.queueSizeRejectionThreshold 即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝。因为maxQueueSize不能被动态修改,这个参数将允许我们动态设置该值。if maxQueueSize == -1,该字段将不起作用
hystrix.threadpool.default.keepAliveTimeMinutes 如果corePoolSize和maxPoolSize设成一样(默认实现)该设置无效。如果通过plugin(https://github.com/Netflix/Hystrix/wiki/Plugins)使用自定义实现,该设置才有用,默认1.
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds 线程池统计指标的时间,默认10000
hystrix.threadpool.default.metrics.rollingStats.numBuckets 将rolling window划分为n个buckets,默认10
3.3Hystrix四种fallback条件
1.任务超过了"withExecutionTimeoutInMilliseconds"定义的超时时间;
2.任务抛出了非HystrixBadRequestException的异常;
3.超过了线程池线程/信号量数目;
4.熔断器打开;
4Hystrix Dashboard 监控
除了隔离依赖服务的调用以外,Hystrix 还提供了准实时的调用监控(Hystrix Dashboard),Hystrix 会持续记录所有通过 Hystrix 发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求,有多少成功或者失败等。Spring Cloud 也提供了 Hystrix Dashboard 的整合,对监控内容转化成可视化界面。
由于当前项目没用到有兴趣的可以去看看,在此给出链接:
https://blog.youkuaiyun.com/eson_15/article/details/86673843