TODO
- 视频看到1.7,代码没跟了 ```properties 大概率的事件使用,极小的性能损耗完成
小概率事件,使用兜底方案
<a name="Ea09e"></a>
# 1. Hystrix的体系架构和核心功能
<a name="cJa54"></a>
## 1.1 没有服务容错的一些场景
<a name="FNjit"></a>
### 1.1.1 服务雪崩
服务雪崩:由最上游请求超时,导致所有链路全部崩溃
<a name="J8Vxi"></a>
### 1.1.2 容器线程耗尽
容器线程耗尽:某个服务请求过于超时,缓慢积累会导致线程池内所有的线程都被该请求占用(使用容器级别的线程隔离技术)
<a name="nv66F"></a>
## 1.2 服务容错的解决方案
如何降低服务故障影的影响
1. 隔离异常服务:线程隔离,给每个不同的请求分配指定的线程,系统不会应为某个请求超时,影响其它请求
1. 减压:快速失败(熔断),部分请求直接返回既定好的失败信息
1. 备选方案:服务降级,对于需要保障的请求,提供多种方案 ,降级的极致就是最小可用性
<a name="tyr8x"></a>
### 1.2.1 服务降级![image.png](https://cdn.nlark.com/yuque/0/2022/png/26687455/1647149546735-16fd1029-8ea8-4fc7-995c-7d413a6247c6.png#clientId=u6c8c1170-adfd-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=305&id=u62b42006&margin=%5Bobject%20Object%5D&name=image.png&originHeight=610&originWidth=1246&originalType=binary&ratio=1&rotation=0&showTitle=false&size=121002&status=done&style=none&taskId=u9a37ce37-0425-4215-bd90-af36d981a1a&title=&width=623)
<a name="Q6lCp"></a>
### 1.2.2 服务熔断
![image.png](https://cdn.nlark.com/yuque/0/2022/png/26687455/1647150480604-92e01bc9-4aa7-4676-945f-a6c3fc235653.png#clientId=u6c8c1170-adfd-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=300&id=u4549f880&margin=%5Bobject%20Object%5D&name=image.png&originHeight=600&originWidth=1326&originalType=binary&ratio=1&rotation=0&showTitle=false&size=133452&status=done&style=none&taskId=uf6649510-7310-480a-8489-81cd107fbfd&title=&width=663)
1. 熔断器是一个开关,Hystrix通过一些配置来判断是否开启还是关闭
1. 当开启的时候,服务请求不会直接去请求服务,而是直接请求FallBack
1. 熔断能有效解决QPS(Query Per Second,每秒访问请求,用来衡量当前系统压力)激增导致的系统雪崩效应
<a name="nEI5m"></a>
### 1.2.2 线程隔离![image.png](https://cdn.nlark.com/yuque/0/2022/png/26687455/1647146432486-49616f52-39ce-4f3d-9058-c46f2af8119c.png#clientId=u6c8c1170-adfd-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=320&id=ue98a0993&margin=%5Bobject%20Object%5D&name=image.png&originHeight=640&originWidth=1336&originalType=binary&ratio=1&rotation=0&showTitle=false&size=164895&status=done&style=none&taskId=u04772901-29de-4d92-97f9-74c30a9b258&title=&width=668)
<a name="wznte"></a>
# 2. 服务降级
<a name="la09J"></a>
## 2.1 降级的写法
```java
@HystrixCommand(fallbackMethod = "putInPrison")
public String bigTiger() {
throw RuntimeException("Eat People");
}
// @HystrixCommand直接作用在需要降级的服务请求上,没有这个注解的则不需要服务将降级
// fallbackMethod = "putInPrison":降级的处理流程,注意降级方法如果有参数也是需要写的
// @HystrixCommand更多参数信息,查看RequestCatch解析
@FeignClient(name = "feign-service-provider", fallback = Fallback.class)
public interface MyHelloService extends HelloService {
}
// Fallback处理这个请求的降级流程:这里有没有@HystrixCommand表明这个接口里面的方法都需要走降级业务
2.2 降级的原理分析
2.2.1 原理分析
- @HystrixCommand:标识该注解,这个方法被Hystrix管理
- Aspect切面拦截
- RequestCatch:请求缓存,请求缓存的cel
- 如果处于开启状态,则尝试使用CatchKey从本地缓存中获取数据,也就不用发起方法调用了
- 如果关闭状态,则走下面的流程
- 注册Observer(观察者模式):方法正常执行、抛出异常、或者结束等状态,都有对应的回调函数
- 发起调用:在发起调用之前,会检查熔断状态,如果是开启的状态则直接fallback,否则发起调用
请求异常:异常则会触发第4步中注册的回调函数,然后直接转给fallback
2.2.2 RequestCatch
该功能并不是让你在fallback的方法里面去访问缓存信息,而是Hystrix会将结果进行缓存
- 该功能是通过@CacheResult、@CacheKey两个注解完成的
- 在一个Hystrix上下文中,如果使用相同的参数对@CacheResult修饰的方法多次调用,Hystrix只会在首次向服务节点发送请求,而后续的请求则是读取第一次的缓存信息
RequestCatch更像是一种性能优化(当然所有的缓存都是为了性能优化而生) ```java @Service public class service{
@Autowired privite IService iservice;
@CacheResult // @CacheResult:意思是该方法的结果可以被Hystrix缓存起来 @HystrixCommand(fallbackMethod = “putInPrison”, commandkey = “catchKey”) public Friend requestCache(@CacheKey Integer id) { // @CacheKey:指这个缓存的结果的业务id System.out.prinlt(“请求….”) } }
// commandkey = “catchKey”,中的catchKey可以替换方法级别的超时时间配置的key // hystrix.command.MyService#retry(Integer).execution.isolation.thread.timeoutInMilliseconds = 3000
// 必须是同一个类
// 如何开启上下文(说实话这个开启就是只能在一个请求中,如果这个请求能操作两次数据,可以使用到缓存,比较鸡肋,想要多次使用,需要扩大范围) HystrixRequestContext context = HystrixRequestContext.initializeContext();
```properties
## 是否开启缓存(默认打开的,无需配置)
hystrix.command.default.requestCatch.enabled = true
2.3 降级的常用方案
参考文章:https://class.imooc.com/lesson/1241#mid=29759
2.3.1 静默处理
2.3.2 默认值
- 主链路容忍性差
2.3.3 想办法恢复服务
(1) 缓存异常
- 假如因为缓存故障无法访问数据,在fallback中的逻辑中可以转而访问底层数据库(这个方法不可以使用在热点数据上,否则可能把数据库打挂,或者引起更大的降级或者熔断,谨慎使用)
但是可以反过来,数据库挂了,可以先访问缓存,但是注意数据一致性
(2) 切换备库
一般大型系统都会做主从+备库的方式做灾备,假如主数据库发生了故障,切换到备用数据库。
-
(3) 重试
ribbon可以处理超时重试,但对于异常情况来说(比如当前资源被锁定),可以在fallback中自己尝试重新发起接口调用
(4) 人工干预
对于机器重要的接口,对异常不能容忍,这里可以借用fallback启动人工干预流程
- 比如日志打点,通过监控组件触发报警,人工干预
2.3.4 多次降级
2.4 代码实战
2.4.1 简单的降级配置
异常降级、超时降级(超时配置看下面)(1) 基础配置
```properties开启fegin下面的hystrix功能(默认开启的,不用配置)
feign.hystrix.enable = true
是否开启服务降级(默认开启的,不用配置)
hystrix.command.default.fallback.enable = true
<a name="qrBE1"></a>
#### (2) 超时配置
```properties
## 开启全局超时配置
hystrix.command.default.execution.timeout.enable = true
## 设置超时时间(毫秒),设置超时时间为两秒,注意等于设置的时间也算超时
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 2000
## 超时以后线程终止
hystrix.command.default.execution.isolation.thread.interruptOnTimeout = true
## 取消的时候线程终止
hystrix.command.default.execution.isolation.thread.interruptOnFutureCancel = true
## 思考?feign和hystrix都设置重试时间如何处理?下面的设置是feign的时间远大于hystrix,具体配置信息查看feign章节
feign-service-provider.ribbon.OkToRetryOnAllOperations = false
feign-service-provider.ribbon.ConnectTimeout = 1000
feign-service-provider.ribbon.ReadTimeout = 8000
feign-service-provider.ribbon.MaxAutoRetries = 0
feign-service-provider.ribbon.MaxAutoRetriesNextServer = 0
## 为方法级别的设置超时时间,这个配置会覆盖全局配置-------------------
## default换成具体的方法信息:类名#方法名称(变量类型)
hystrix.command.MyService#retry(Integer).execution.isolation.thread.timeoutInMilliseconds = 3000
## 有个函数可以自动生成这个替换信息,是通过反射的方式
Feign.configKey(类名.class, 类名.class.getMethod("方法名称",参数类型.class))
feign.configKey(MyService.class, MyService.class.getMethod("retry",int.class))
## 通过commandKey配置,查看requestCatch章节
## 注解配置看下面图片
2.4.2 多级降级
一直降级还是不能解决,直接熔断
@HystrixCommand(fallbackmethod = “”),直接指定就行了
2.5 Hystrix和Ribbon超时冲突
- https://class.imooc.com/lesson/1241#mid=29764
3. 服务熔断
参考文章:https://class.imooc.com/lesson/1241#mid=29767
hystrix的原生配置都可在这里查询 https://github.com/Netflix/Hystrix/wiki/Configuration ```properties开启/关闭熔断的功能(默认 true)
hystrix.command.default.circuitBreaker.enable = true
强制开启熔断开关(默认 = false),如果打开了,所有正常调用的服务也会开启熔断
hystrix.command.default.circuitBreaker.forceOpen = false
强制关闭熔断开关(默认 = false)
hystrix.command.default.circuitBreaker.forceClosed = false
```properties
## 窗口时间,根据设置的值来统计这个时间内异常请求,默认值是毫秒级别,默认值是5000
## 示例配置的含义是:以每隔20秒的窗口来统计异常请求数量
hystrix.command.default.metrics.rollingStats.timeInMilliseconds = 20000
## 熔断的前提条件(指请求的数量)
## 示例配置的含义是:在一定的时间窗口内,请求达到5个以后,才开始进行熔断判断
hystrix.command.default.circuitBreaker.requestVolumeThreshold = 5
## 熔断的前提条件(指异常请求在所有请求中的占比)
## 示例配置的含义是:在一定的时间窗口内,异常请求达到50%,才开始进行熔断判断
hystrix.command.default.circuitBreaker.errorThresholdPercentage = 50
## 当熔断开启之后,经过多少秒进入半开状态(这个时间一般要小于窗口时间)
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds = 10000
4. 线程隔离
https://class.imooc.com/lesson/1241#mid=30013
https://class.imooc.com/lesson/1241#mid=30014