问题
你是否在项目中使用线程池遇到过以下问题?
1.创建线程池核心参数不好评估,随着业务流量的波动,极有可能出现生产故障。
2.不支持优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。
3.不支持运行时监控,使用过程中业务无响应,不知道是不是线程池引起。
4.三方框架 RocketMQ、Dubbo 等线程池无法动态修改参数,修改后只能重启应用。
在真实业务场景中,线程池可能遇到的问题比这里描述的还要多,稀奇古怪。所以采用动态线程池—Hippo4j。
功能
通过对 JDK 线程池的增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保
障能力。
Hippo4j 框架提供以下功能支持:
1. 客户端应用运行时实时变更指定线程池核心参数,变更生效支持集群和单实例两种方式。
2. 线程池运行时异常报警,比如:线程池活跃度、阻塞队列容量水位较高,触发了拒绝策略以
及任务运行时间超长等。
3. 定时任务(默认 5 秒)采集线程池运行数据,可上报 Prometheus、InfluxDB 等数据库,
搭配 Grafana 做大屏展示。
4. 运行过程中支持实时查看线程池当前运行状态以及线程池内线程的堆栈信息。
5. 支持 Tomcat、Undertow 和 Jetty 容器线程池运行时查看和动态变更线程池配置。
6. 支持 Dubbo、Hystrix、Kafka、RabbitMQ、RocketMQ 等客户端线程池运行时数据查看
和动态变更线程池配置。
应用场景
1. 动态调参
Google 或者百度搜索线程池和生产事故关键字,几页都放不下,这也间接说明了线程池是个很
考验使用者技术功底的技术点。
那有没有一些技巧或者技术来尽量规避线程池使用上的问题?比如:线程池的配置应该如何选
择?
对于线程池参数的纠结点主要有两个,无外乎设置的线程数多了或者少了:
1. 如果预设的线程数或阻塞队列数量少了,当业务量上来,任务都在排队或者执行拒绝策
略。
2. 如果超量设置线程池的参数,无疑会造成资源浪费。
如果要修改运行中应用线程池参数,需要停止线上应用,调整成功后再发布,而这个过程异常的
繁琐,如果能在运行中动态调整线程池的参数无疑会提高问题解决效率。
Hippo4j 提供了应用线程池运行时变更核心参数的功能。而且,如果应用是集群部署,可以选择
修改线程池某一实例,或者修改集群全部实例,运行时生效,不需要再重启服务。
压测时可以使用 Hippo4j 动态调整线程池参数,判断线程池核心参数设置是否合理。对于开发
测试来说,如果不满足可以随时调整。
2. 告警策略
很多时候,线程池出故障的时候,系统已经发生了很严重的损失。有没有一种方式,在使用的线
程池即将出现问题,但还算比较可控时,触发相关报警提示给用户,进而规避该问题?
Hippo4j 基于上述问题思考,集成了四种报警策略:
1. 活跃度:假设阈值设置 80%,线程池最大线程数 10,当线程数达到 8 发起报警。
2. 阻塞队列容量:假设阈值设置 80%,阻塞队列容量 100,当容量达到 80 发起报警。
3. 触发拒绝策略:当线程池任务触发了拒绝策略时,发起拒绝策略报警。
4. 任务运行超时:假设用户设置单个任务正常执行是 1000ms,实际执行超过该时间发起报
警。
3. 线程池监控
Hippo4j 线程池提供了两种监控方式:线程池运行时数据采集监控以及客户端线程池运行实时状
态查看。
线程池运行时数据采集适合应用负责人巡查应用健康状态和排查问题时使用,实时状态适合排查
多实例之间的运行数据状态。
4. 框架底层线程池
上面讲的动态线程池是业务中开发人员手动创建的线程池,比如下面这个:
@Bean
@DynamicThreadPool
public ThreadPoolExecutor messageConsumeDynamicExecutor() {
String threadPoolId = "message-consume";
return ThreadPoolBuilder.builder()
.threadFactory(threadPoolId)
.threadPoolId(threadPoolId)
.dynamicPool()
.build();
}
而框架线程池指的是某些三方中间件底层使用到的线程池,比如 Dubbo、RocketMQ 等框架,这些底层框架为了增强性能选择使用线程池进行扩展。
为什么要适配这些中间件框架的线程池?相信这是很多小伙伴的疑问。以 Dubbo 举例,当服务高并发调用时,如果 Dubbo 底层线程池没有经过个性化配置,极有可能导致线程池打满,最终导致无法提供服务。
当遇到这种情况,可以使用 Hippo4j 对 Dubbo 线程池进行核心参数调整,避免生产故障时间持续。
再举个例子,当 RocketMQ 消息积压时,可能大部分公司的解决方案是添加客户端应用节点。而这种方式虽然可以解决问题,但是问题也很明显,太复杂且资源浪费。完全可以调整RocketMQ SDK 底层线程池的线程数来达到快速消费的逻辑,有效解决 MQ 消息堆积问题。
目前 Hippo4j 已支持的三方中间件线程池列表:
Apache Dubbo
Alibaba Dubbo
Apache Kafka
Apache RocketMQ
RabbitMQ
SpringCloud Stream RocketMQ
SpringCloud Hystrix
Tomcat
Jetty
Undertow
上述中间件线程池都可以在 Hippo4j 页面上操作核心参数动态变更以及监控功能,如下所示: