故障描述
案例1:
Case1:2018年XX页面展示接口大量调用降级: 事故描述:XX页面展示接口产生大量调用降级,数量级在几十到上百。 事故原因:该服务展示接口内部逻辑使用线程池做并行计算,由于没有预估好调用的流量,导致最大核心数设置偏小,大量抛出RejectedExecutionException,触发接口降级条件,示意图如下 https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html
案例2:
Case2:2018年XX业务服务不可用S2级故障 事故描述:XX业务提供的服务执行时间过长,作为上游服务整体超时,大量下游服务调用失败。 事故原因:该服务处理请求内部逻辑使用线程池做资源隔离,由于队列设置过长,最大线程数设置失效,导致请求数量增加时,大量任务堆积在队列中,任务执行时间过长,最终导致下游服务的大量调用超时失败。示意图如下: https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html
故障原因
线程池的各类参数设置不合理,导致提交的任务执行失败。
1、线程池的corePoolSize、maximumPoolSize
不可能一开始就设置的很精确,必须动态调整。
2、线程池的workQueue
一定要使用容量限制的队列类型。无限制容量的队列,可能会由于任务执行时间过长,没有超时获取,导致OOM的发生。
3、线程池的RejectedExecutionHandler
可以参考博文
Java并发指南:ThreadPoolExecutor配置RejectedExecutionHandler指南及避坑
小结
1、线程池的各个参数必须设置的要合理,必要时可引入消息中间件,抵御流量高峰,或者降级为走消息,慢慢处理业务。
2、完备的报警机制,线程池执行拒绝策略时,必须报警。线程池数目达到最大线程池设置时,可以报警。线程池队列要满时,可以报警提示。
3、要有降级策略,避免级联故障。
4、业务隔离,可以单独服务处理。
5、自动扩容策略,是最保险的策略,业务遇到流量高峰,自动扩容。
6、动态设置线程池参数,但不是要随意设置数目巨多的线程池数目,线程也占用资源,数目过多也会导致OOM的发生,可以参考5。
以上几点,是个人目前认为几个比较重要的,防御性避免线程故障发生。