大家好,我是马克。
线程池是一个基于池的思想来管理线程的工具。使用线程池可以降低创建和销毁线程的成本,避免过多线程导致的系统资源耗尽。线程池的使用在高并发和大量任务处理场景中是必不可少的。

如果你在项目中实际使用线程池,相信你可能会遇到以下痛点:
线程池随便定义,线程资源太多,导致服务器负载高。
线程池参数不好评估,随着业务的并发改善,业务有失败的风险。
线程池任务执行时间超过平均执行周期,开发者是察觉不到的。
线程任务的累积触发拒绝策略,影响现有服务的正常运行。
当业务出现超时、熔断等问题时,由于没有监控,无法确定是否是线程池导致的。
本机线程池不支持运行时变量的传输,比如当MDC上下文遇到线程池时的GG。
无法执行正常关机。当项目关闭时,大量正在运行的线程池任务被丢弃。
线程池运行时,任务执行停止,疑似死锁或耗时操作,但无从下手。
基于以上诸多痛点,Mark开始了hippo4j的开发,致力于构建一个中间件框架,用于标准线程池的动态变化和监控。
hippo4j是什么?
Hippo4j增强了JDK ThreadPoolExecutor的线程池,扩展了三方框架底层的线程池,提高了业务系统的在线运行保障能力。
提示:hippo4j不仅有Java ThreadPoolExecutor的增强,还有Dubbo、RabbitMQ、RocketMQ、Hystrix、Tomcat、Jetty、Undertow等框架线程池的监控和动态改变。
hippo4j适合什么场景?
1.线程池是任意定义的,导致服务器负载高。
在系统开发过程中,由于涉及到多人的协作,难免会出现信息不互通的情况。在同一个系统中,任意定义线程池是很常见的。
开发者张三定义了user-log-record-thread-pool来记录用户操作日志。
开发者李四要记录成员操作日志,定义了成员-日志-记录-线程-池;
王武定义了power-log-record-thread-pool来记录权限操作日志;
……
随着系统的不断发展,定义了越来越多语义相同或不同的线程池,间接导致了服务器资源的严重消耗。
但如果在系统中使用hippo4j,可以在控制台上查看当前应用的已有线程池,是否存在语义相同、服务可重用的线程池实例,避免线程资源的过度浪费。
2.线程池参数不容易评估
业务中使用线程池,十个程序员中有九个可能会担心。如何选择这个线程池的配置?
我觉得纠结点主要有两点,不外乎设置的数量是多还是少。
如果预设的线程数或者阻塞队列数很小,当业务量上来的时候,会遇到两种情况,无论哪种都是业务无法接受的。
估计200ms完成的任务可能2s就能完成,因为任务都是排队的。
任务完成后,执行拒收政策,影响正常业务。
如果线程池的参数设置过多,无疑会造成资源的浪费,也会导致两种情况。
线程也会占用服务器资源,多开会给服务器造成一定压力。
如果创建了太多线程,那么在使用其他线程池执行时,服务器CPU上下文切换也是一个问题。
众所周知,如果要修改正在运行的应用的线程池参数,需要停止在线应用,调整成功后再发布,而且这个过程极其繁琐。如果能在运行过程中动态调整线程池参数就好了。
基于这些痛点,美团的技术团队引入了动态线程池的概念,诞生了包括hippo4j在内的多个动态线程池框架。
如果应用程序是集群部署,hippo4j可以选择修改线程池的一个实例,或者修改集群的所有实例,这将在运行时生效,无需重新启动服务。
比如在压力测试时使用hippo4j动态调整线程池参数,对于开发测试也是一个不错的选择。
3.线程池运行时警报策略
从线程池运行时监控来看,hippo4j内置了四种报警策略,分别是线程池活跃度、阻塞队列容量、拒绝策略触发和任务运行超时报警。
线程池的活跃度:假设阈值设置为80%,线程池中的最大线程数为10。当线程数达到8时,会发出警报。
阻塞队列容量:假设阈值设置为80%,阻塞队列容量为100。当容量达到80时,会发出警报。
触发拒绝策略:当线程池任务触发拒绝策略时,它会启动拒绝策略警报。
任务超时:假设单个任务的超时时间为1000ms,超过这个时间执行任务就会启动报警。
Hippo4j支持钉钉、企业微信和舒菲软件通知、线程池任务运行超时报警示例:
4.线程池的运行时状态对开发人员来说是黑箱。
线程池对于服务运行过程中的开发者来说是一个完整的黑匣子。开发人员无法知道线程池参数的变化,比如阻塞队列的数量或者完成任务的数量,对故障排除不友好。
Hippo4j支持实时查看线程池的运行时状态,在核心参数的基础上扩展了负载、内存、拒绝次数等关键指标。每个查询返回线程池的当前运行信息。
5.线程池监控
Hippo4j提供了两种方式来监控线程池的运行时数据,即:
1.内置数据池数据采集+监控,不依赖任何中间件,以hippo4j内部集成的方式运行。
2.使用三方中间件Prometheus或ElasticSearch+Grafana进行收集和监控。
6.集成Spring ThreadPoolTaskExecutor
Spring ThreadPoolTaskExecutor将一些功能扩展到了原生线程池。我觉得还有两个比较实用的,hippo4j已经支持了。

当服务停止时,线程池被通知处理剩余的任务,并在等待指定时间后强制停止。
将线程上下文传递给线程池执行上下文。
第一个是实际使用中的核心函数,降低了线程池中丢弃任务的可能性。这里,重点强调一下。
我们平时停止应用的时候,有没有这样的考虑?线程池中的所有任务真的都完成了吗?
也许完成了,也许没有。
基于以上考虑,Spring注册了线程池销毁方法。当应用程序关闭时,如果发现线程池中有未完成的任务,需要等待指定的时间。如果在规定时间内完成任务执行,大家都会开心;如果有未完成的任务,丢弃它们。
你为什么放弃任务而不是等待?
如果长时间执行线程池任务,会影响整个应用的停止,所以做一个折中。
7.三方框架中间件的线程池适配
hippo4j的目标是兼容所有框架的线程池,它可以提供监控和动态修改的能力。
当前支持的三方框架线程池列表:
阿帕奇·杜博
阿里·杜博
兔子q
Apache RocketMQ
SpringCloud Stream RocketMQ
SpringCloud Hystrix
雄猫
码头
逆流
支持上述框架的线程池的动态参数变化和监控功能,如:
未来,hippo4j将支持更多三方框架线程池。如果你有好的想法,也可以和我交流。
8.线程池运行堆栈视图
当线程池正在运行时,任务停止运行,并且怀疑存在死锁或耗时的操作。大多数程序员会选择使用命令或arthas来检查线程池中正在运行的线程的堆栈,以查看工作线程卡在了哪个方法中。
基于以上痛点,hippo4j推出了线程池运行栈实时查看功能。
9.动态线程池会影响性能吗?
这可能是很多开发者担心的一点。这里统一回复一下吧。
Hippo4j只是增强了线程池的一些核心功能,并没有修改任务执行源代码的进程,可以保证绝对的安全性。
其次,hippo4j的上述功能扩展在线程池执行任务的主进程之外,不会影响线程池的正常执行性能。
hippo4j支持两种操作模式
Hippo4j为用户提供了两种操作模式,即轻量级的配置中心访问和功能更加完善的服务器端访问。以下是各自的优缺点。
1.hippo4j配置
依靠配置中心完成线程池的动态变化。支持的配置中心有:Nacos、Apollo、Zookeeper,并且会连接到Etcd、Consul等。在未来。
另外,hippo4j已经支持用户自定义配置中心的实现,如果使用公司自研或其他配置中心,可以用最小的工作量引入。
使用hippo4j配置模式的优点和缺点:
优点:可以根据项目中已有的配置中心,使用轻量级的引入来集成hippo4j,无需引入其他服务就可以使用线程池参数动态、运行时监控、报警等核心功能。
不足:由于缺乏可视化控制台页面,上述许多功能无法使用。
2.hippo4j服务器
您需要部署hippo4j Jar包,它涵盖了上面描述的所有功能。
因为配置中心和注册中心是在服务器内部实现的,它不依赖于任何三方中间件。
优点:功能齐全,可以享受更多的服务和便利。如果应用程序启动一个集群,你可以指定一个实例的线程池修改,而配置是整个集群的改变。
缺点:与hippo4j config相比,需要额外部署一个jar包,增加了部署工作量。
如果您最初使用hippo4j config并希望切换到server,那么当它们被替换时,不需要修改业务代码。
建议:根据公司情况选择。如果基本功能能满足要求,选择hippo4j config使用。如果想要更多的功能,可以选择hippo4j服务器。
项目的近期发展
开源项目的发展离不开用户和贡献者的支持。Mark梳理了hippo4j的近况:
GitHub,Gitee reap 3.2k+星,810+叉。
2022.4.12 Gitee选择GVP。
58个项目贡献者为hippo4j做出了贡献。给你一个大大的感谢。
注册公司16家,制作环境正式运行hippo4j。
通过墨菲的安全扫描,不存在代码安全漏洞。

文章的结论
总之,开源作者每天下班后和周六周日都牺牲时间做开源项目。如果觉得有用,感谢以下两个平台的支持,星~
吉图布:https://github.com/opengoofy/hippo4j
吉蒂:https://gitee.com/agentart/hippo4j
资料来源:https://www.cnblogs.com/longtaiblog/p/16623110.html


