在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性
一、Hystrix宗旨和工作原理
Hystrix的宗旨:
- 防止任何单个依赖项耗尽所有容器(如Tomcat)用户线程
- 在任何可行的地方提供回退,以保护用户不受失败的影响
- 使用隔离技术(如隔离板、泳道和断路器模式)来限制任何一个依赖项的影响
- 通过近实时的度量、监视和警报来优化发现时间
- 通过配置的低延迟传播来优化恢复时间
- 避免在整个依赖客户端执行中出现故障,而不仅仅是在网络流量中
Hystrix工作原理
当需要完成某项任务时,通过 Hystrix 将任务包裹起来,交由 Hystrix 来完成任务,从而享受 Hystrix 带来保护
- 1、构建命令
Hystrix 提供了两个Command, HystrixCommand 和 HystrixObservableCommand,可以使用这两个对象来包裹待执行的任务,Hystrix应用自己的一系列保护机制,在执行用户任务的各节点(执行前、执行后、异常、超时等)做一系列的事情
- 2、执行命令
有四种方式执行command
- R execute():同步执行,从依赖服务得到单一结果对象
- Future queue():异步执行,返回一个 Future 以便获取执行结果,也是单一结果对象
- Observable observe():hot observable,创建Observable后会订阅Observable,可以返回多个结果
- Observable toObservable():cold observable,返回一个Observable,只有订阅时才会执行,可以返回多个结果
- 3、检查缓存
如果启用了 Hystrix Cache,任务执行前将先判断是否有相同命令执行的缓存。如果有则直接返回缓存的结果;如果没有缓存的结果,但启动了缓存,将缓存本次执行结果以供后续使用
- 4、检查断路器是否打开
断路器(circuit-breaker)和保险丝类似,保险丝在发生危险时将会烧断以保护电路,而断路器可以在达到我们设定的阀值时触发短路(比如请求失败率达到50%),拒绝执行任何请求。如果断路器被打开,Hystrix 将不会执行命令,直接进入Fallback处理逻辑
- 5、检查线程池/信号量情况
Hystrix 隔离方式有线程池隔离和信号量隔离。当使用Hystrix线程池时,Hystrix 默认为每个依赖服务分配10个线程,当10个线程都繁忙时,将拒绝执行命令。信号量同理
- 6、执行具体的任务
通过
HystrixObservableCommand.construct()
或者HystrixCommand.run()
来运行用户真正的任务
- 7、计算链路健康情况
每次开始执行command、结束执行command以及发生异常等情况时,都会记录执行情况,例如:成功、失败、拒绝以及超时等情况,会定期处理这些数据,再根据设定的条件来判断是否开启断路器
- 8、命令失败时执行 Fallback 逻辑
在命令失败时执行用户指定的 Fallback 逻辑。上图中的断路、线程池拒绝、信号量拒绝、执行执行、执行超时都会进入 Fallback 处理
- 9、返回执行结果
原始结果将以Observable形式返回,在返回给用户之前,会根据调用方式的不同做一些处理
更多详细的工作原理,可前往: How is Works
二、HystrixCommand注解
我们的配置都是基于 HystrixCommand 的,我们通过在方法上添加 @HystrixCommand 注解并配置注解的参数来实现配置,但有的时候一个类里面会有多个 Hystrix 方法,每个方法都是类似配置的话会冗余很多代码,这时候我们可以在类上使用 @DefaultProperties 注解来给整个类的 Hystrix 方法设置一个默认值。
参数介绍
- commandKey: 用来标识一个 Hystrix 命令,默认会取被注解的方法名。需要注意:Hystrix 里同一个键的唯一标识并不包括 groupKey,建议取一个独一二无的名字,防止多个方法之间因为键重复而互相影响
- groupKey:一组Hystrix命令的集合,统计、报告、默认取类名,可不配置
- threadPoolKey:标识一个线程池,如果没设置会取groupKey,很多情况下都是同一个类内的方法共用一个线程池
- commandProperties:与此命令相关的属性
- threadPoolProperties:与线程池相关的属性
- observableExcutionMode:当Hystrix命令被包装成RxJava的Observer异步执行时,此配置指定了Observable被执行的模式,默认是
ObservableExecutionMode.EAGER
, Obserable会在被创建后立刻执行,而ObservableExecutionMode.EAGER
模式下,则会产生一个Observable被Subscribe执行,此配置项可以不配置 - ignoreExceptions:默认Hystrix在执行方法时捕获到异常时执行回退,并统计失败率以修改熔断器的状态,而被忽略的异常则会直接抛到外层,不执行回退,也不影响熔断器的状态
- raiseHystrixExceptions:当配置项包括 HystrixRuntimeException 时,所有的未被忽略的异常都会被包装成HystrixRuntimeException,配置其他种类的异常好像并没有什么影响
- fallbackMethod:方法执行时容断、错误、超时会执行的回退方法,需要
保证返回值一致
三、hystrix配置属性详解
Hystrix配置属性详解,所有属性默认都是default,如需针对某个服务,将default改为那个服务名即可
- Execution:控制HystrixCommand.run()如何运行
- Fallback:控制HystrixCommand.getFallback()如何运行
- Circuit Breadker:控制断路器的行为
- Metrics:捕获HystrixCommand和HystrixObervableCommand执行消息相关的配置属性
- Request Context:设置请求上下文的属性
- Collapser Properties:设置请求合并的属性
- Thread Pool Properties:设置线程池的属性
1、Execution
-
execution.isolation.strategy:表示Hystrix.command.run()执行时的隔离策略,有以下两种
- Thread:在单独的线程上执行,并发请求受线程池中的线程数限制
- SEMAPHORE:在调用线程上执行,并发请求量受信号量计数限制
在默认情况下,推荐HystrixCommands 使用 thread 隔离策略,HystrixObservableCommand 使用 semaphore 隔离策略。 只有在高并发(单个实例每秒达到几百个调用)的调用时,才需要修改HystrixCommands 的隔离策略为semaphore 。semaphore 隔离策略通常只用于非网络调用
hystrix.command.default.execution.isolation.strategy=
- execution.isolation.thread.timeoutInMilliseconds:设置调用者执行的超时实际(单位毫秒),默认1000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=
- execution.isolation.thread.interruptOnTimeout:表示设置执行超时时,中断HystrixCommand.run() 的执行,默认true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=
- execution.isolation.semaphore.maxConcurrentRequests:当HystrixCommand.run()使用SEMAPHORE隔离策略时,设置并发量,默认10
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=
- execution.isolation.thread.interruptOnCancel:表示设置是否在取消任务执行时,中断HystrixCommand.run()执行,默认false
hystrix.command.default.execution.isolation.thread.interruptOnCancel=
2、Fallback
以下属性控制HystrixCommand.getFallback() 如何执行。这些属性对隔离策略THREAD 和SEMAPHORE都起作用
- fallback.isolation.semaphore.maxConcurrentRequests:此属性设置从调用线程允许HystrixCommand.getFallback()方法允许的最大并发请求数 如果达到最大的并发量,则接下来的请求会被拒绝并且抛出异常,默认10
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=
- fallback.enabled:是否开启fallback功能,默认true
hystrix.command.default.fallback.enabled=
3、Circuit Breaker
控制断路器的行为
- circuitBreaker.enabled:是否开启断路器功能,默认true
hystrix.command.default.circuitBreaker.enabled=
- circuitBreaker.requestVolumeThreshold:使断路器跳闸的最小请求数量,如果此属性值为20,则在窗口时间内(如10s内),如果只收到19个请求且都失败了,则断路器也不会开启。默认20
hystrix.command.default.circuitBreaker.requestVolumeThreshold=
- circuitBreaker.errorThresholdPercentage:设置失败百分比的阈值。如果失败比率超过这个值,则断路器跳闸并且进入fallback逻辑,默认50
hystrix.command.default.circuitBreaker=
- circuitBreaker.forceOpen:如果设置true,则强制使断路器跳闸,则会拒绝所有的请求.此值会覆盖circuitBreaker.forceClosed的值,默认false
hystrix.command.default.circuitBreaker.forceOpen=
- circuitBreaker.forceClosed:如果设置true,则强制使断路器进行关闭状态,此时会允许执行所有请求,无论是否失败的次数达到circuitBreaker.errorThresholdPercentage值,默认false
hystrix.command.default.circuitBreaker.forceClosed=
4、Mertrics
捕获和HystrixCommand 和 HystrixObservableCommand 执行信息相关的配置属性
- metrics.rollingStats.timeInMilliseconds:设置统计滚动窗口的时间长度如果此值为10s,将窗口分成10个桶,每个桶表示1s时间,默认10000
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=
- metrics.rollingStats.numBuckets:设置统计滚动窗口的桶数量,
必须满足metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0
,否则异常,比如:1000/10,1000/20是正确的,1000/7是错的,高并发环境里,每个桶时间长度建议大于100ms
,默认10
hystrix.command.default.metrics.rollingStats.numBuckets=
- metrics.rollingPercentile.enabled:设置执行延迟是否被跟踪,并且被计算在失败百分比中。如果设置为false,则所有的统计数据返回-1,默认true
hystrix.command.default.metrics.rollingPercentile.enabled=
- metrics.rollingPercentile.timeInMilliseconds:此属性设置统计滚动百分比窗口的持续时间,默认60000
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds=
- metrics.rollingPercentile.numBuckets:设置统计滚动百分比窗口的桶数量,以下配置必须成立,
metrics.rollingPercentile.timeInMilliseconds % metrics.rollingPercentile.numBuckets == 0
,否则异常,比如1000/10是对的,1000/7是错的,高并发情况下每个桶的时间长度建议大于1000ms
,默认6
hystrix.command.default.metrics.rollingPercentile.numBuckets=
- metrics.rollingPercentile.bucketSize:设置每个桶保存的执行时间的最大值。如果桶数量是100,统计窗口为10s,如果这10s里有500次执行,只有最后100次执行会被统计到bucket里去,默认100
hystrix.command.default.metrics.rollingPercentile.bucketSize=
- metrics.healthSnapshot.intervalInMilliseconds:采样时间间隔
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds=
5、Request Context
此属性控制HystrixCommand使用到的Hystrix的上下文
- requestCache.enabled:是否开启请求缓存功能,默认true
hystrix.command.default.requestCache.enabled=
- requestLog.enabled:是否开启日志,打印执行HystrixCommand的情况和事件,默认true
hystrix.command.default.requestLog.enabled=
6、Collapser Properties
设置请求合并的属性
- maxRequestsInBatch:设置同时批量执行的请求的最大数量,默认Integer.MAX_VALUE
hystrix.collapser.default.maxRequestsInBatch=
- timerDelayInMilliseconds:批量执行创建多久之后,再触发真正的请求,默认10
hystrix.collapser.default.timerDelayInMilliseconds=
- requestCache.enabled:是否对HystrixCollapser.execute() 和 HystrixCollapser.queue()开启请求缓存,默认true
hystrix.collapser.default.requestCache.enabled=
7、Thread Pool Properties
设置Hystrix Commands的线程池行为,大部分情况线程数量是10。线程池数量的计算公式如下:最高峰时每秒的请求数量 × 99%命令执行时间 + 喘息空间
,设置线程池数量的主要原则是保持线程池越小越好
,因为它是减轻负载并防止资源在延迟发生时被阻塞的主要工具
- coreSize:设置线程池的core的大小,默认10
hystrix.threadpool.default.coreSize=
- maximumSize:设置最大的线程池的大小,只有设置allowMaximumSizeToDivergeFromCoreSize时,此值才起作用,默认10
hystrix.threadpool.default.maximumSize=
- maxQueueSize:设置最大的BlockingQueue队列的值。如果设置-1,则使用SynchronousQueue队列,如果设置正数,则使用LinkedBlockingQueue队列,默认-1
hystrix.threadpool.default.maxQueueSize=
- queueSizeRejectionThreshold:因为maxQueueSize值不能被动态修改,所有通过设置此值可以实现动态修改等待队列长度。即等待的队列的数量大于queueSizeRejectionThreshold时(但是没有达到maxQueueSize值),则开始拒绝后续的请求进入队列。如果设置-1,则属性不启作用,默认5
hystrix.threadpool.default.queueSizeRejectionThreshold=
- keepAliveTimeMinutes:设置线程多久没有服务后,需要释放(maximumSize-coreSize )个线程,默认1
hystrix.threadpool.default.keepAliveTimeMinutes=
- allowMaximumSizeToDivergeFromCoreSize:设置allowMaximumSizeToDivergeFromCoreSize值为true时,maximumSize才有作用,默认false
hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize=
- metrics.rollingStats.timeInMilliseconds:设置滚动窗口的时间,默认10000
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds=
- metrics.rollingStats.numBuckets:设置滚动静态窗口分成的桶的数量,必须满足
metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0
,默认值10,高并发情况下建议每个桶时间长度大于100ms
hystrix.threadpool.default.metrics.rollingStats.numBuckets=
四、Hystrix的使用
1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>cn.gjing</groupId>
<artifactId>tools-httpclient</artifactId>
<version>1.0.2</version>
</dependency>
2、启动类标注注解
/**
* @author Gjing
*/
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);
}
}
3、编写service
@Service
public class TestService {
// 此注解说明该方法要执行回退,fallback指定回退方法
@HystrixCommand(fallbackMethod = "defaultFallback")
public String hello() {
HttpClient httpClient = new HttpClient();
return httpClient.get("http://127.0.0.1:8090/test2", String.class);
}
// 回退方法
public String defaultFallback() {
return "no hello";
}
}
4、编写Controller进行测试
/**
* @author Gjing
**/
@RestController
public class TestController {
@Resource
private TestService testService;
@PostMapping("/test")
public String test() {
return testService.hello();
}
}
5、目标服务的接口
因为hystrix默认超时是1秒,所以,我们在目标服务,加个线程休眠,大于1秒即可
/**
* @author Gjing
**/
@RestController
public class DemoController {
@GetMapping("/test2")
public String test2() throws InterruptedException {
Thread.sleep(1000);
return "ok";
}
}
5、测试
超时进入回退方法,并返回了回退方法里的内容
三、Feign使用Hystrix
1、增加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、启动类增加@EnableFeignClients
注解
3、增加一个service和回退类
/**
* @author Gjing
* name为目标服务名,fallback为回退类
**/
@FeignClient(name = "demo",fallback = FeignServiceFallbackImpl.class)
public interface FeignService {
@RequestMapping(value = "/test2", method = RequestMethod.GET)
String test2();
}
/**
* 回退类,实现feignService
*/
@Component
class FeignServiceFallbackImpl implements FeignService{
@Override
public String test2() {
return "啊哦,出错了");
}
}
4、编写Controller进行测试
/**
* @author Gjing
**/
@RestController
public class FeignController {
@Resource
private FeignService feignService;
@PostMapping("/testFeign")
public String testFeign() {
return feignService.test();
}
}
如果出错了,会进行回退
四、使用监控面板dashBoard
1、增加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2、启动类加上注解@EnableHystrixDashboard
3、配置文件
server:
port: 8082
spring:
application:
name: hystrix-demo
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# feign使用hystrix进行回退
feign:
hystrix:
enabled: true
# 端点管理 hystrixDashboard
management:
endpoints:
web:
exposure:
include: "*"
4、启动项目,并访问http://localhost:port/hystrix即可打开如下页面
通过主页面的文字介绍,可以知道,共支持三种不同的监控方式*
- 默认的集群监控,通过URL:http://turbine-hostname:port/turbine.stream开启监控
- 指定的集群监控,通过URL:http://turbine-hostname:port/turbine.stream?cluster=[clusterName]开启监控
- 单体应用的监控,通过URL:http://hystrix-app:port/actuator/hystrix.stream开启监控
本文只讲解单体应用,因此我们在主页面输入http://localhost:8082/actuator/hystrix.stream即可访问,请求几次结果
界面解释
到此本文就结束了,篇幅较长,如哪里有字写错或单词打错,各位帮忙纠正,本项目源码:SpringCloud-Demo