在分布式环境中, 许多服务依赖项中的一些必然会失败. 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
来源: https://yq.aliyun.com/articles/707172