Hystrix使用和源码分析
1丶Hystrix介绍
微服务架构中存在的问题
由于调用链中的某个服务出现问题,引起整个系统的雪崩
Hystrix是什么
Hystrix是用于处理延迟和容错的开源库
Hystrix的作用是怎么
- 服务隔离丶服务熔断丶服务降级(快速失败)
- 限流
- 请求合并和请求响应
- 自带单体和集群监控
2丶Hystrix架构图
3丶Hystrix环境搭建
(1)引入依赖
4丶HystrixCommand四种使用方式
实现类
各个使用方式区别
execute()方式(同步执行)
queue()方式(异步执行)
obServer()方式
- 阻塞式调用
- 非阻塞式调用
toObserver
- 阻塞调用
- 非阻塞调用
5丶HystrixObserver Command 演示
- 实现类
- observer()方式
- toObserver()方式
- 总结
6丶Hystrix配置之GroupKey和CommandKey
GroupKey:
- Hystrix中GroupKey是唯一必填项
- GroupKey可以作为分组监控和报警作用
- GroupKey将作为线程池的默认名称
CommandKey:
- Hystrix可以不填写CommandKey
- 默认Hystrix会通过反射类名命名CommandKey
- 在Setting中加入AndCommandKey进行命名
7丶Hystrix请求缓存
Hystrix支持将请求结果进行本地缓存
通过实现GetCacheKey方法来判断是否取出缓存
请求缓存要求必须在同一个上下文
可以用RequestCacheEnable开启请求缓存
(1) 测试方法
(2) 重写获得缓存Key方法
(3) 缓存开关(默认打开)
总结
8丶Hystrix请求合并
- Hystrix支持将多个请求合并成一次请求
- Hystrix请求合并要求两次请求必须足够“近”
- 请求合并分为局部合并和全局合并两种
- 通过Collapser可以设置相关参数
8.2、请求合并对象的构建
/** * 泛型参数: 批量返回类型,批量处理中每次处理中响应类型,批量处理中每次请求参数类型 */ public class CommandCollapser extends HystrixCollapser<List<String>,String ,Integer> { private Integer id; public CommandCollapser(Integer id){ super(Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey("CommandCollapser"))); this.id =id; } /** * 获取请求参数 * @return */ @Override public Integer getRequestArgument() { return id; } /** * 批量业务处理 * @param collection * @return */ @Override protected HystrixCommand<List<String>> createCommand(Collection<CollapsedRequest<String, Integer>> collection) { return new BatchCommand(collection); } /** * 批量处理结果与请求业务之间映射关系处理 * @param strings * @param collection */ @Override protected void mapResponseToRequests(List<String> strings, Collection<CollapsedRequest<String, Integer>> collection) { int count = 0; Iterator<CollapsedRequest<String, Integer>> iterator = collection.iterator(); while(iterator.hasNext()){ CollapsedRequest<String, Integer> request = iterator.next(); String result = strings.get(count++); request.setResponse(result); } } class BatchCommand extends HystrixCommand<List<String>>{ private Collection<CollapsedRequest<String, Integer>> collection; public BatchCommand(Collection<CollapsedRequest<String, Integer>> collection){ super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("BatchCommand"))); this.collection = collection; } @Override protected List<String> run() throws Exception { System.err.println("currentThread: " + Thread.currentThread().getName()); List<String> result = Lists.newArrayList(); Iterator<CollapsedRequest<String, Integer>> iterator = collection.iterator(); while (iterator.hasNext()){ CollapsedRequest<String, Integer> request = iterator.next(); Integer reqParam = request.getArgument(); // 具体业务请求 result.add("msb request:" + reqParam); } return result; } } }
总结
9丶Hystrix隔离术
Hystrix隔离命名ThreadPoolkey
- Hystrix可以不填写ThreadPoolKey
- 默认Hystrix会使用GroupKey命名线程池
- 在Setting中加入andThreadPoolKey进行命名
Hystrix隔离介绍
- Hystrix提供了信号量和线程两种隔离手段
- 线程隔离会在单独的线程中执行业务逻辑
- 信号量隔离在调用线程上执行
- 官方推荐优先线程隔离
Hystrix隔离演示
- 我们直接执行CommandTest中的execute
打印出线程名称 My thread ,这说明1、我们的指定是生效的 2、我们现在用的是线程池中的线程
My thread-1 也就是线程池里面的第一个线程
更改线程模式
再运行,看结果线程是main 主线程
线程池和信号量的区别
线程隔离
- 应用自身完全受保护,不会受其他依赖影响
- 有效降低接入新服务的风险
- 依赖服务出现问题,应用自身可以快速反应问题
- 可以通过实时刷新动态属性减少依赖问题影响
信号量隔离
- 信号量隔离是轻量级的隔离术
- 无网络开销的情况推荐使用信号量隔离
Hystrix线程隔离和信号量隔离参数解析及演示
(1)线程隔离参数演示
线程池相关配置:
(2) 信号量演示
信号量隔离配置:
设置为信号量模式
创建线程
class MyThread extends Thread{ private String name; public MyThread(String name){ this.name = name; } @Override public void run() { CommandDemo commandDemo = new CommandDemo(name); System.out.println("commandDemo result="+commandDemo.execute()); } }
参数设置
执行就会报错如下报信号量不足的问题
10丶Hystrix快速失败与降级
降级介绍
- 降级就是当服务无法返回正常结果的时候,返回一个默认的值
- Command降级需要实现fallback方法
- ObservableCommand降级实现resumeWithFallback方法
重写降级内容
运行
总结
降级触发原则
- 熔断器处于开启状态
- 线程池或信号量已满
- HystrixBadRequestException以外的异常
- 运行超时
看一下图
快速失败
- Hystrix提供了快速失败的机制 【Hystrix提供了快速失败的机制 ,这个机制就是依赖我们的
fallback,当出现任何异常都会触发fallback,fallback就会把失败的业务逻辑返回给前端】- 当不实现fallback方法时会将异常直接抛出
11丶Hystrix熔断器介绍和使用
Hystrix熔断器介绍
- 熔断器是一种开关,用来控制流量是否执行业务逻辑
- 熔断器核心指标:快照时间窗
- 熔断器核心指标:请求总数阈值
- 熔断器核心指标:错误百分比阈值
熔断器的开启有两种方式,1、通过指标来判断是否开启 2、手动开启关闭 ,一般我们都是基于指标
来开启,不会手动开启关闭我们看一下这个图,熔断器的底层算法——滑动窗口
熔断器状态
- 熔断器开启:所有请求都会进入fallback方法
- 熔断器半开启:间歇性让请求触发run方法
- 熔断器关闭:正常处理业务请求
- 默认情况下熔断器开启5秒后进入半开启状态
图的演示:
Hystrix熔断器演示
熔断机制相关配置更改对应测试代码
@Test public void CBTest() throws InterruptedException { // 正确 - 业务 CommandDemo c1 = new CommandDemo("msb-demo"); System.out.println(c1.execute()); // 错误 - 业务 CommandDemo c2 = new CommandDemo("error-demo"); System.out.println(c2.execute());
// 正确 - 业务 CommandDemo c3 = new CommandDemo("msb-demo"); System.out.println(c3.execute());
}
更改业务代码,让其报错误异常![image-20230612201912287](https://img-blog.csdnimg.cn/55665a11be934570aefd055be2a7b174.png) + 测试强制开启参数![image-20230612201953871](https://img-blog.csdnimg.cn/272f63d4d4dc492cac47e8c94896e3d4.png) + 开启后全部降级![image-20230612202021474](https://img-blog.csdnimg.cn/f7c867b80afd435ea34c05ef7d97e69d.png) + 半开状态(更改参数)![image-20230612202057739](https://img-blog.csdnimg.cn/c7da79374dfa478d9fcf07e5b397ba5a.png) 因为默认情况下熔断器开启5秒后进入半开启状态,所以我们需要先修面5秒后才能进入半开启 状态 进入半开启状态 如果第一次调用失败,则熔断器再次进入开启状态, 如果第一次调用成功,则 熔断器处于关闭状态 ![image-20230612202145397](https://img-blog.csdnimg.cn/21d1aea8925d414d986929184c6f95bf.png)
12丶Hystrix环境集成丶整合Feign以及线程大小设置
- Hystrix环境集成
引入依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> </dependencies>
开启Hystrix
增加HystrixCommand注解
@HystrixCommand(fallbackMethod = "fallbackMethod", commandProperties = { @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"), @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value= "200"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50") }, threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "1"), @HystrixProperty(name = "maxQueueSize", value = "10"), @HystrixProperty(name = "keepAliveTimeMinutes", value = "1000"), @HystrixProperty(name = "queueSizeRejectionThreshold", value = "8"), @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"), @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1500") }) public String fallbackMethod(String userId){ return "服务降级返回默认用户信息 userId:" + userId; }
数据测试
由于我们超时时间是200ms,我们让他休眠1s,这样都出现超时,就会出现降级
- Hystrix整合Feign
开启Feign
feign:
hystrix:
enabled: true降级内容实现
@Service public class UserFeignClientFallBack implements UserFeignClient { @Override public String getUser(String userId) { return "获取用户失败进行降级:" + userId; } }
降级配置
根据具体错误信息进行降级
降级FallBackFactory
@Component public class UserFeignClientFallBackFactory implements FallbackFactory<UserFeignClient> { @Override public UserFeignClient create(Throwable cause) { return new UserFeignClient() { @Override public String getUser(String userId) { String message = cause.getMessage(); return "根据不同的异常进行不同到的处理方式"; } }; } }
进行配置
3丶实战技巧:如何设置线程池
首先呢,我们一定要明白一个概念,就是在我们业务系统中呢,有一个很重要的衡量指标叫QPS,
当然我们一般说QPS的其实是一个统称,更精确地说他其
4丶总结