Skip to main content

03小节:为什么需要动态线程池?

作者:程序员马丁

在线博客:https://nageoffer.com

note

热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。

为什么需要动态线程池,元数据信息:

©版权所有 - 拿个offer-开源&项目实战星球专属学习项目,依据《中华人民共和国著作权法实施条例》《知识星球产权保护》,严禁未经本项目原作者明确书面授权擅自分享至 GitHub、Gitee 等任何开放平台。违者将面临法律追究。


内容摘要:本文分析了线程池管理的常见痛点,包括资源浪费、参数评估风险、任务堆积、监控缺失及优雅关闭难题。同时,介绍了 oneThread 框架通过优化线程池资源复用、支持动态参数调整、实时报警与监控、优雅关闭机制等关键特性,全方位提升系统的稳定性和效率。

课程目录如下所示:

  • 线程池通用痛点
  • oneThread 如何解决痛点?
  • 文末总结

线程池通用痛点

  • 线程池随便定义,线程资源过多,造成服务器高负载。

  • 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。

  • 线程池任务堆积,任务执行超时或触发拒绝策略,影响既有业务正常运行。

  • 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。

  • 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。

oneThread 如何解决痛点?

1. 线程池资源管理

在系统开发的过程中,因为涉及到多人协作,难免会出现信息不互通的情况。在同一个系统,对于线程池来说,常见的是线程池随意定义。

  • 开发者张三要记录用户操作日志,定义了 user-log-record-thread-pool
  • 开发者李四要记录会员操作日志,定义了 member-log-record-thread-pool
  • 开发者王五要记录权限操作日志,定义了 power-log-record-thread-pool
  • ……

随着系统不断开发,相同或不同语义的线程池被定义的越来越多,间接导致服务器资源严重耗损。

而如果系统中使用 oneThread,能够在控制台或配置中心查看当前应用已有线程池,是否存在相同语义且业务可复用线程池实例,避免线程资源过度浪费。

2. 线程池参数动态变更

业务中使用了线程池,十个程序员可能有九个都在犯嘀咕,这线程池的配置应该如何选择?

我觉得犯纠结的点主要有两个,无外乎设置的数多了或者少了。

  1. 如果预设的线程数或阻塞队列数量少了,当业务量上来,会遇到两种情况,不管哪一种对业务来说都是不能接受的。
    1. 预估 200ms 执行完的任务,可能会 2s 执行完,因为任务都在排队。
    2. 任务满了后,开始执行拒绝策略,影响正常业务。
  2. 如果超量设置线程池的参数,无疑会造成资源浪费,同样会造成两种情况。
    1. 线程资源也是占用服务器资源的,开启的多了对服务器有一定压力。
    2. 如果过多的创建线程,当和其它线程池一起执行时,服务器 CPU 上下文切换也是个问题。

大家都知道,如果要修改运行中应用线程池参数,需要停止线上应用,调整成功后再发布,而这个过程异常的繁琐,如果能在运行中动态调整线程池的参数多好。

美团技术团队基于这些痛点,推出了动态线程池的概念,催生了一批动态线程池框架,Hippo4j 也是其一。而马哥基于 Hippo4j 的设计思路,抽象出了更精简和易懂的 oneThread 框架。

若修改上述参数,变更请求会通过 dashboard-dev 服务组装参数并调用 Nacos 接口,更新对应的配置文件。各客户端应用通过监听 Nacos 配置中心,可实现线程池配置的实时刷新。

线程池变更钉钉通知消息:

压测时可以使用 oneThread 动态调整线程池参数,判断线程池核心参数设置是否合理。对于开发测试来说,如果不满足可以随时调整。

3. 运行时通知告警

从线程池运行时监控的角度出发,oneThread 内置 3 种报警策略,线程池活跃度、阻塞队列容量以及拒绝策略触发报警。

  • 线程池活跃度:假设阈值设置 80%,线程池最大线程数 10,当线程数达到 8 发起报警。
  • 阻塞队列容量:假设阈值设置 80%,阻塞队列容量 100,当容量达到 80 发起报警。
  • 触发拒绝策略:当线程池任务触发了拒绝策略时,发起拒绝策略报警。

当前 oneThread 支持钉钉告警,线程池任务运行超时报警示例:

也可按需支持企微和飞书,接口扩展点已保留,设计非常人性。

4. 线程池运行监控

线程池监控并非锦上添花,而是高并发应用的核心基础设施之一。日常开发中,线程池虽然提升了系统性能,但隐藏的风险却不容忽视:

  • 线程池任务排队过长、拒绝任务时,业务可能出现大面积超时甚至瘫痪,但如果缺乏监控机制,开发人员很难在故障初期发现问题根源。

  • 监控能及时识别异常,帮助技术人员第一时间介入处理,降低业务受损程度。

  • 未经监控的线程池往往存在资源浪费(线程数过多)或性能瓶颈(线程数过少)。

oneThread 内部提供了两种监控方式:线程池核心参数监控以及线程池实例运行时状态检查。

4.1 线程池核心参数监控

该页面依托 Prometheus 存储和采集线程池监控数据,并通过 Grafana 进行可视化展示。

4.2 线程池实例运行时状态

线程池运行时数据实时采集展示。

通过两种监控方式,可以方便快捷掌握线程池运行时的数据状态,可以用作健康巡查以及历史问题复盘。

5. 优雅关闭防任务丢失

项目关闭(重新发布)时,线程池中的任务全部完成了么?相信这个问题大家都有个大大的问号,如果你用的原生线程池并且没有做任何防控措施,答案是否定的,可能会有数据丢失。

那如何保障数据在项目关闭时,线程池数据运行结束呢?

@Bean
@DynamicThreadPool
public ThreadPoolExecutor onethreadProducer() {
return ThreadPoolExecutorBuilder.builder()
.threadPoolId("onethread-producer")
.corePoolSize(2)
.maximumPoolSize(4)
.keepAliveTime(9999L)
.awaitTerminationMillis(5000L)
.workQueueType(BlockingQueueTypeEnum.SYNCHRONOUS_QUEUE)
.threadFactory("onethread-producer_")
.rejectedHandler(new ThreadPoolExecutor.CallerRunsPolicy())
.dynamicPool()
.build();
}

参数 awaitTerminationMillis 代表了什么意思?当项目关闭时,如果线程池中还有数据,等待任务完成的时间,单位毫秒。

问题很多的小伙伴可能就问了:为什么会有 awaitTerminationMillis 参数,等待全部任务完成不就好了么?

线程池中都是执行很快的任务可能是没问题。但是,如果线程池里面都是耗时的任务呢?停止项目时等个几分钟甚至更长时间是无法忍受的。这个需要根据大家项目的实际情况评估。

文末总结

总而言之,线程池作为现代应用中的关键资源,其不当管理常引发服务器高负载、任务响应延迟、甚至业务故障等一系列问题。

oneThread 框架在开源动态线程池的思路上,构建了一套闭环解决方案:它通过统一资源管理平台减少线程池冗余;借助动态配置引擎实现参数实时变更;结合多维度告警(如活跃度、队列容量和拒绝策略触发)确保问题早发现;提供基于 Prometheus 和 Grafana 的监控视图助力健康巡检;并针对项目发布时的关闭风险,引入 awaitTerminationMillis 参数实现可容忍数据丢失的优雅关闭。

这套体系不仅简化了开发运维流程,更通过可扩展设计(支持钉钉、企微等告警),为业务在高并发场景下的持续稳定运行提供了可靠保障。

完结,撒花 🎉