Hystrix是Netfilx开源的一款针对分布式系统延迟和容错的框架,目的是用来隔离分布式故障,它提供了线程和信号量隔离,可以减少不同服务竞争资源造成的影响,提供了优雅降级机制以及熔断机制来保持高可用。
当大多数人在使用Tomcat时,多个HTTP服务会共享一个线程池,假设其中一个HTTP 服务访问的数据库响应非常慢,这将造成服务响应时间延迟增加,大多数线程阻塞等待数据响应返回,导致整个Tomcat线程池都被该服务占用,甚至拖垮整个Tomcat。因此,如果我们能把不同HTTP服务隔离到不同的线程池,则某个HTTP服务的线程池满了也不会对其他服务造成灾难性故障。这就需要线程隔离或者信号量隔离来实现了。
线程池隔离
下面来看使用Hystrix实现线程隔离:
看上图,tomcat会为不同的http服务设置不同的线程池,同时为不同的分布式服务调用设置不同的线程池。
假设我们要调用一个获取问题的服务。可以通过一个命令来实现,我们可以把其包装成一个命令模式,模式中继承HystrixCommand,重写线程隔离以及配置的一些信息。代码如下:
public class HystrixProblemServiceCommand extends HystrixCommand> {
// 组名
private static final String GROUP_KEY = "qa";
// 命令标识名
private static final String COMMAND_KEY = "query";
// 线程池名
private static final String THREADPOOL_KEY = "qa-pool";
@Autowired
private ProblemService problemService;
protected HystrixProblemServiceCommand() {
super(setter());
}
private static Setter setter() {
// 服务分组
HystrixCommandGroupKey hystrixCommandGroupKey = HystrixCommandGroupKey.Factory.asKey(GROUP_KEY);
// 服务唯一标识
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(COMMAND_KEY);
// 线程池名称
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey(THREADPOOL_KEY);
// 线程池配置
HystrixThreadPoolProperties.Setter thresholdConfig = HystrixThreadPoolProperties.Setter()
.withCoreSize(10) // 核心线程池大小
.withKeepAliveTimeMinutes(5) // 空闲线程存活时间
.withMaxQueueSize(Integer.MAX_VALUE) // 线程池队列大小
.withQueueSizeRejectionThreshold(1000); //限定当前队列大小
// 命令属性配置,设置隔离策略为线程隔离
HystrixCommandProperties.Setter isolationStrategy = HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
return HystrixCommand.Setter
.withGroupKey(hystrixCommandGroupKey)
.andCommandKey(commandKey)
.andThreadPoolKey(threadPoolKey)
.andThreadPoolPropertiesDefaults(thresholdConfig)
.andCommandPropertiesDefaults(isolationStrategy);
}
@Override
protected List run() throws Exception {
return problemService.findAll();
}
}
一些重要的组件分析:
HystrixCommandGroupKey: 配置全局唯一 标识服务分组的名称,比如,库存系统就是一 个服务分组。当我们监控时,相同分组的服务会聚合在一 起,必填选项。
HystrixCommandKey: 配置全局唯一 标识服务的名称,比如,库存系统有一 个获取库存服务,那么就可以为这个服务起一 个名字来唯一 识别该服务,如果不配置,则默认是简单类名。
HystrixThreadPoolKey: 配置全局唯一标识线程池的名称,相同线程池名称的线程池是同一 个,如果不配置,则默认是分组名,此名字也是线程池中线程名字的前缀。
HystrixThreadPoolProperties: 配置线程池参数,coreSize配置核心线程池大小和线程池最大大小,keepAliveTimeMinutes是线程池中空闲线程生存时间(如果不进行动态配置,那么是没有任何作用的),maxQueueSize配置线程池队列最大大小,queueSizeRejectionThreshold限定当前队列大小,即实际队列大小由这个参数决定,通过改变queueSizeRejectionThreshold可以实现动态队列大小调整。
HystrixCommandProperties: 配置该命令executionlsolationStrategy
配置执行隔离策略,默认是使用线程隔离,此处我们配置为THREAD, 即线程池隔离。
此处可以粗粒度实现隔离,也可以细粒度实现隔离.
服务分组+线程池:粗粒度实现,一个服务分组配置一个线程池,配置线程池名称或者相同分组的线程池名称配置为一 样。
服务分组+服务+线程池:细粒度实现, 一个服务分组中的每一 个服务配置一 个隔离线程池,为不同的命令实现配置不同的线程池名称即可。
混合实现:一个服务分组配置一个隔离线程池,然后对重要服务单独设置隔离线程池。
在实际的应用中,如果要动态改变其中的一些参数,配置,则可以使用如下方式去动态修改:
例如要修改线程池配置:
String dynamicQueueSizeRej ectionThreshold = "hystrix. threadpool. " + "qa-pool"
+ ".queueSizeRejectionThreshold";
Configuration configuration = ConfigurationManager. getConfiginstance ();
configuration.setProperty(dynamicQueueSizeRejectionThreshold, 100);
同理:
如果是改变线程池配置,则是"hystrix.threadpool." + threadPoolKey + propertyName;
如果是改变命令属性配置,则是"hystrix.command." + commandKey + propertyName。
调用时:
可以选择同步调用或者异步调用。
还有一种是配置RxJava实现响应式编程,这里不做过多概述。
信号量隔离
另外还有一个隔离方式是信号量隔离,就是在HystrixCommandProperties: 配置该命令executionlsolationStrategy
配置执行隔离策略如下:
信号量隔离只是限制了总的并发数,服务使用主线程进行同步调用,即没有线程池。因此,如果只是想限制某个服务的总并发调用量或者调用的服务不涉及远程调用的话,可以使用轻量级的信号量来实现。
待续。。。
喜欢可以关注我