Hystrix提供了两种依赖隔离方式:线程池隔离 和 信号量隔离。两种隔离方式都是限制对共享资源的并发访问量,
- 线程在就绪状态、运行状态、阻塞状态、终止状态间转变时需要由操作系统调度,占用很大的性能消耗;
- 而信号量是在访问共享资源时,进行tryAcquire,tryAcquire成功才允许访问共享资源。
线程池隔离。Hystrix可以为每一个依赖建立一个线程池,使之和其他依赖的使用资源隔离,同时限制他们的并发访问和阻塞扩张。每个依赖可以根据权重分配资源(这里主要是线程),每一部分的依赖出现了问题,也不会影响其他依赖的使用资源。
实现这种隔离的线程池,需要使用到@HystrixCommand注解提供的其他属性。接下来,我们会:
- 为方法getLicensesByOrg()设置一个隔离的线程池
- 设置该线程池的线程数
- 设置队列的容量,该队列的作用是当线程池中的线程都处于工作状态,接下来的请求会进入该队列。如下所示
@HystrixCommand(
fallbackMethod = "buildFallbackLicenseList",
threadPoolKey = "licenseByOrgThreadPool",
threadPoolProperties = {
@HystrixProperty(name = "coreSize",value="30"),
@HystrixProperty(name="maxQueueSize", value="10")
}
)
public List<License> getLicensesByOrg(String organizationId){
randomlyRunLong();
return licenseRepository.findByOrganizationId(organizationId);
}
优点:
- 一个依赖可以给予一个线程池,这个依赖的异常不会影响其他的依赖。
- 使用线程可以完全隔离第三方代码,请求线程可以快速放回。
- 当一个失败的依赖再次变成可用时,线程池将清理,并立即恢复可用,而不是一个长时间的恢复。
- 可以完全模拟异步调用,方便异步编程。
- 使用线程池,可以有效的进行实时监控、统计和封装。
缺点:
- 使用线程池的缺点主要是增加了计算的开销。每一个依赖调用都会涉及到队列,调度,上下文切换,而这些操作都有可能在不同的线程中执行。
信号量隔离
信号量的资源隔离只是起到一个开关的作用,例如,服务X的信号量大小为10,那么同时只允许10个tomcat的线程(此处是tomcat的线程,而不是服务X的独立线程池里面的线程)来访问服务X,其他的请求就会被拒绝,从而达到限流保护的作用。
execution.isolation.strategy: "SEMAPHORE"
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 3000
execution.isolation.semaphore.maxConcurrentRequests: 100
二者的比较
线程池隔离 | 信号量隔离 | |
---|---|---|
线程 | 与调用线程非相同线程 | 与调用线程相同(jetty线程) |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 可以是异步,也可以是同步。看调用的方法 | 同步调用,不支持异步 |
并发支持 | 支持(最大线程池大小hystrix.threadpool.default.maximumSize) | 支持(最大信号量上限maxConcurrentRequests) |
是否超时 | 支持,可直接返回 | 不支持,如果阻塞,只能通过调用协议(如:socket超时才能返回) |
是否支持熔断 | 支持,当线程池到达maxSize后,再请求会触发fallback接口进行熔断 | 支持,当信号量达到maxConcurrentRequests后。再请求会触发fallback |
隔离原理 | 每个服务单独用线程池 | 通过信号量的计数器 |
资源开销 | 大,大量线程的上下文切换,容易造成机器负载高 | 小,只是个计数器 |