分布式环境中咱们不能确保调用的依赖服务不发生问题。诸如基础设施服务器宕机(数据库、缓存),外部依赖服务挂掉,网络抖动引发的链接超时。当这些状况发生时,大量请求迅速涌到链路上的故障节点,就会像繁忙的高速公路上发生交通事故短期内就会形成交通拥堵,直至瘫痪。html
不能让局部服务的故障不断扩散蔓延致使全局系统不可用,形成重大生产事故,因此要对外部依赖进行资源隔离,将故障控制在小范围内。java
建立副线程去调用依赖服务, 执行依赖代码的线程与请求线程(好比Tomcat线程)分离 ,当副线程请求调用失败、超时等异常状况而阻塞时不会影响到主线程。算法
能够利用线程可设置超时的特性给经过网络进行的服务间调用设置超时时间。spring
线程池机制的优缺点数据库
线程池机制的优势:后端
(1) 任何一个依赖服务均可以被隔离在本身的线程池内,即便本身的线程池资源填满了,也不会影响任何其余的服务调用;缓存
(2) 服务能够随时引入一个新的依赖服务,由于即便这个新的依赖服务有问题,也不会影响其余任何服务的调用;tomcat
(3) 当一个故障的依赖服务从新变好的时候,能够经过清理掉线程池,瞬间恢复该服务的调用,而若是是tomcat线程池被占满,再恢复就很麻烦;服务器
(4) 若是一个client调用配置有问题,线程池的健康情况随时会报告,好比成功/失败/拒绝/超时的次数统计,而后能够近实时热修改依赖服务的调用配置,而不用停机;网络
(5) 若是一个服务自己发生了修改,须要从新调整配置,此时线程池的健康情况也能够随时发现,好比成功/失败/拒绝/超时的次数统计,而后能够近实时热修改依赖服务的调用配置,而不用停机;
(6) 基于线程池的异步本质,能够在同步的调用之上,构建一层异步调用层;
线程池机制的缺点:
(1) 线程池机制最大的缺点就是增长了cpu的开销。
用于隔离本地代码,利用信号量限制同时运行的线程数量,不会建立副线程,开销较小(复杂算法、多重循环,时间复杂度高的状况)。
hystrix利用线程池的工做原理来进行限流。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize
为线程池的基本大小。maximumPoolSize
为线程池最大线程大小。keepAliveTime
和 unit
则是线程空闲后的存活时间。workQueue
用于存听任务的阻塞队列。handler
当队列和最大线程池都满了以后的饱和策略。四种拒绝策略
AbortPolicy:不处理,直接抛出异常。
CallerRunsPolicy:若线程池还没关闭,调用当前所在线程来运行任务,r.run()执行。
DiscardOldestPolicy:LRU策略,丢弃队列里最近最久不使用的一个任务,并执行当前任务。
DiscardPolicy:不处理,丢弃掉,不抛出异常。
@HystrixCommand(commandKey = "getCompanyInfoById", groupKey = "company-info", threadPoolKey = "company-info", fallbackMethod = "fallbackMethod", threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "30"), @HystrixProperty(name = "maxQueueSize", value = "101"), @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"), @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"), })
服务提早配置备用措施,当故障发生时无缝启用备用方案(或者返回一些默认值),用户无感知,最终目的都是为了提供7*24小时稳定服务,这样对用户来讲才是高价值、可信赖的优质服务。
线程run()抛出异常,超时,线程池或信号量满了,或短路了,都会触发fallback机制。
当请求调用失败率达到阀值自动触发降级(如因网络故障/超时形成的失败率高),熔断器触发的快速失败会进行快速恢复。
工做流程:
根据监控结果能够看出合理的超时时长配置和计算出服务集群部署的规模大小。
计算公式:
服务集群部署的规模大小 = 服务在健康状态时每秒支撑的最大请求数 * 第99百分位延迟时间(以秒为单位)+ 少许用于缓冲的额外线程
举个例子:某个服务须要支撑的QPS为1000,即每秒须要处理1000个请求。假设经过统计获得百分之99的服务耗时为100ms,那一个线程1秒内能处理10个请求,一台机器的线程池大小通常就使用默认配置为10,即一台机器1秒内能处理 10 * 10 = 100个请求,最后得出该服务总共须要部署 1000/100 = 10台机器。
在一次请求中,若是有多个command,参数都是同样的,调用的接口也是同样的,其实结果能够认为也是同样的。这个时候,可让第一次command执行返回的结果,被缓存在内存中,而后在这个请求上下文中,后续的其余对这个依赖的调用所有从内存中取用缓存结果就能够了。
https://blog.csdn.net/zhuchua...
高并发的场景下,将一个时间窗口内多个相同请求合并成一个请求,较少网络链接次数。
http://blog.didispace.com/spr...
默认状况下,hystrix不会将父线程的上下文传播到有hystrix命令管理的线程中。例如,在默认状况下,对被父线程调用并由@HystrixCommand保护的方法而言,在父线程中设置为ThreadLocal的值是不会自动传递到子线程的。实际场景中的例子,调用通用UserUtil工具类从请求头获取不到userId, jwt串, 租户id。
两种方法:
(1) 手动设置
手动从父线程ThreadLocal中取出须要的key-value设置到子线程中。
(2) 代码配置
自定义HystrixConcurrencyStrategy