性能文章>聊聊动态线程池的9个场景>

聊聊动态线程池的9个场景原创

419347

大家好,我是小马哥。

线程池是一种基于 池化思想管理线程 的工具,使用线程池可以减少 创建销毁线程的开销,避免线程过多导致 系统资源耗尽。在 高并发以及大批量 的任务处理场景,线程池的使用是必不可少的。

如果有在项目中实际使用线程池,相信你可能会遇到以下痛点:

  • 线程池随便定义,线程资源过多,造成服务器高负载。
  • 线程池参数不易评估,随着业务的并发提升,业务面临出现故障的风险。
  • 线程池任务执行时间超过平均执行周期,开发人员无法感知。
  • 线程池任务堆积,触发拒绝策略,影响既有业务正常运行。
  • 当业务出现超时、熔断等问题时,因为没有监控,无法确定是不是线程池引起。
  • 原生线程池不支持运行时变量的传递,比如 MDC 上下文遇到线程池就 GG。
  • 无法执行优雅关闭,当项目关闭时,大量正在运行的线程池任务被丢弃。
  • 线程池运行中,任务执行停止,怀疑发生死锁或执行耗时操作,但是无从下手。

基于以上诸多痛点,小马哥着手 hippo4j 的开发,致力于打造标准线程池 动态变更 和 监控 的中间件框架。

GitHub:https://github.com/opengoofy/hippo4j

Gitee:https://gitee.com/agentart/hippo4j

什么是 hippo4j

hippo4j 通过对 JDK ThreadPoolExecutor 线程池增强,以及扩展三方框架底层线程池等功能,为业务系统提高线上运行保障能力。

image1.png

小贴士:hippo4j 不止于 Java ThreadPoolExecutor 的增强,Dubbo、RabbitMQ、RocketMQ、Hystrix、Tomcat、Jetty、Undertow 等框架线程池也都有进行监控和动态变更。

什么场景适合用 hippo4j

1. 线程池随意定义,造成服务器高负载

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

开发者张三要记录用户操作日志,定义了 user-log-record-thread-pool;
开发者李四要记录会员操作日志,定义了 member-log-record-thread-pool;
开发者王五要记录权限操作日志,定义了 power-log-record-thread-pool;
……
随着系统不断开发,相同或不同语义的线程池被定义的越来越多,间接导致服务器资源严重耗损。

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

image2.png

2. 线程池参数不易评估

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

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

如果预设的线程数或阻塞队列数量少了,当业务量上来,会遇到两种情况,不管哪一种对业务来说都是不能接受的。
预估 200ms 执行完的任务,可能会 2s 执行完,因为任务都在排队。
任务满了后,开始执行拒绝策略,影响正常业务。
如果超量设置线程池的参数,无疑会造成资源浪费,同样会造成两种情况。
线程资源也是占用服务器资源的,开启的多了对服务器有一定压力。
如果过多的创建线程,当和其它线程池一起执行时,服务器 CPU 上下文切换也是个问题。
大家都知道,如果要修改运行中应用线程池参数,需要停止线上应用,调整成功后再发布,而这个过程异常的繁琐,如果能在运行中动态调整线程池的参数多好。

美团技术团队基于这些痛点,推出了 动态线程池 的概念,催生了一批动态线程池框架,hippo4j 也是其一。

image3.png

如果应用是集群部署,hippo4j 可以选择修改线程池 某一实例,或者修改 集群全部实例,运行时生效,不需要再重启服务。

image4.png

再比如,压测时使用 hippo4j 动态调整线程池参数,对于开发测试来说,也是个不错的选择。

image5.png

3. 线程池运行时报警策略

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

  • 线程池活跃度:假设阈值设置 80%,线程池最大线程数 10,当线程数达到 8 发起报警。
  • 阻塞队列容量:假设阈值设置 80%,阻塞队列容量 100,当容量达到 80 发起报警。
  • 触发拒绝策略:当线程池任务触发了拒绝策略时,发起拒绝策略报警。
    任务运行超时:假设单个任务超时为 1000ms,任务执行超过该时间发起报警。
    hippo4j 支持钉钉、企业微信和飞书软件通知,线程池任务运行超时报警示例:
    image6.png

4. 线程池运行时状态对开发者黑盒

线程池在服务运行过程中,对开发者来说是一个完全的黑盒。开发者无法得知线程池的参数变化,比如阻塞队列数量或者完成任务数等核心参数,这对于排查问题来说并不友好。

hippo4j 支持线程池运行时状态实时查看,并在核心参数的基础上扩展了 负载、内存以及拒绝次数 等关键指标,每次查询返回线程池当前运行信息。
image7.png

5. 线程池监控

hippo4j 提供了两种线程池运行时数据监控方式,分别是:

1、内置数据池数据采集 + 监控,无需依赖任何中间件,由 hippo4j 内部集成的方式运行。

image8.png

2、使用三方中间件 Prometheus 或 ElasticSearch + Grafana 采集和监控。

image9.jpeg

6. 整合 Spring ThreadPoolTaskExecutor

Spring ThreadPoolTaskExecutor 对原生线程池扩展了一部分功能,我认为比较实用有两个,并且 hippo4j 也已经支持。

当服务停止时,通知线程池处理剩余任务,并在等待指定时间后强制停止。
传递线程上下文到线程池执行上下文中。
第一个是实际使用中很核心的功能,减少了线程池丢弃任务的可能,这里重点说明下。

我们平时在停止应用时,有没有这样一个考虑,线程池中的任务真的都执行完成了吗?

可能执行完了,可能没有。

Spring 基于以上考虑,注册了线程池销毁方法。在应用关闭时,如果发现线程池存在任务没有执行完,需要等待一个指定时间。指定时间内任务执行如果执行完毕,皆大欢喜;如果还存在没有结束的任务,则丢弃。

为什么会丢弃任务而不是再等等?

因为如果线程池任务长时间执行,会影响整个应用的停止,进行了折中处理。

7. 三方框架中间件线程池适配

hippo4j 的目标是兼容所有框架的线程池,并可以提供监控和动态修改的能力。

目前已支持的三方框架线程池列表:

Apache Dubbo
Alibaba Dubbo
RabbitMQ
Apache RocketMQ
SpringCloud Stream RocketMQ
SpringCloud Hystrix
Tomcat
Jetty
Undertow

支持上述框架线程池的动态变更参数和监控功能,比如:

image10.png
未来 hippo4j 会支持更多三方框架线程池,如果你有好的想法也可以和我沟通。

8. 线程池运行堆栈查看

线程池运行中,任务运行停止,怀疑发生死锁或执行耗时操作。大多数程序员会选择使用命令或者 arthas 查看线程池运行中线程的堆栈,看看其中的 Worker 都在哪个方法卡住了。

hippo4j 基于以上痛点,推出了线程池运行堆栈实时查看功能。

image11.png

9. 动态线程池对性能有无影响

这可能是很多开发者担心的一个点,在这里统一回复下。

hippo4j 仅对线程池做部分核心功能增强,没有修改任务执行源代码流程,可以保证绝对的安全。

其次,hippo4j 上述的功能,都是与线程池执行任务主流程外扩展的,不会影响线程池正常的执行性能。

hippo4j 支持的两种运行模式

hippo4j 为用户提供了两种运行模式,分别是 轻量级的配置中心接入,和功能更齐全的 服务端接入,下面都来说说各自的优缺点。

1. hippo4j config

依赖配置中心完成线程池的动态变更,已支持的配置中心有:Nacos、Apollo、Zookeeper,未来还会接入 Etcd、Consul 等。

另外,hippo4j 已支持用户自定义配置中心实现,如果使用公司自研或其它配置中心,也可以极小工作量进行引入。

使用 hippo4j config 模式的优点和不足:

优点:轻量级引入,可以根据项目中已有配置中心进行 hippo4j 的集成,无需引入其它服务,即可使用线程池参数动态化、运行时监控、报警等核心功能。
不足:缺少可视化控制台页面,上文中描述的诸多功能不能使用。

2. hippo4j server

需要部署 hippo4j Jar 包,涵盖上文中描述的所有功能。

因为服务端内部实现了配置中心和注册中心(参考 nacos 和 eureka 实现),所以它不依赖任何三方中间件。

优点:功能齐备,可以享受更多的服务和便利。如果应用启动的是集群,可以指定其中某一个实例的线程池修改,而 config 则是整个集群变更。
不足:相比较 hippo4j config,需要额外部署一个 jar 包,增加了部署工作量。
如果最初使用 hippo4j config,想要切换到 server,两者在进行替换的时候,无需修改业务代码。

使用建议:根据公司情况选择,如果基本功能可以满足使用,选择 hippo4j config 使用即可;如果希望更多的功能,可以选择 hippo4j server。

项目发展近况

开源项目发展离不开用户和贡献者的支持,小马哥梳理出最近 hippo4j 发展近况:

GitHub、Gitee 收获 3.2k+ star,810+ fork。
2022.4.12 Gitee 评选 GVP(最有价值开源项目)。
58 名项目贡献者为 hippo4j 添砖加瓦,这里重点感谢。
16 家使用登记公司,生产环境正式运行 hippo4j。
通过墨菲安全扫描,无任何代码安全漏洞隐患。

文末结语

最后总结下,开源作者牺牲了每天下班和周六日的时间做开源项目,如果觉得有用,麻烦各位大佬在以下两个平台 star 支持下,灰常感谢~

GitHub:https://github.com/opengoofy/hippo4j

Gitee:https://gitee.com/agentart/hippo4j
image12.jpeg

点赞收藏
马丁玩编程

公众号:马丁玩编程

请先登录,查看4条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

JDBC PreparedStatement 字段值为null导致TBase带宽飙升的案例分析

JDBC PreparedStatement 字段值为null导致TBase带宽飙升的案例分析

随机一门技术分享之Netty

随机一门技术分享之Netty

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

7
4