马上周末,聊聊动态线程池的 9 个场景 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
machen
V2EX    Java

马上周末,聊聊动态线程池的 9 个场景

  •  6
     
  •   machen
    magestacks 2022-11-03 08:55:58 +08:00 5163 次点击
    这是一个创建于 1083 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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

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

    什么是 hippo4j

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

    小贴士:hippo4j 不止于 Java ThreadPoolExecutor 的增强,DubboRabbitMQRocketMQHystrixTomcatJettyUndertow 等框架线程池也都有进行监控和动态变更。

    什么场景适合用 hippo4j

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

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

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

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

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

    2. 线程池参数不易评估

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

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

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

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

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

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

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

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

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

    • 线程池活跃度:假设阈值设置 80%,线程池最大线程数 10 ,当线程数达到 8 发起报警。
    • 阻塞队列容量:假设阈值设置 80%,阻塞队列容量 100 ,当容量达到 80 发起报警。
    • 触发拒绝策略:当线程池任务触发了拒绝策略时,发起拒绝策略报警。
    • 任务运行超时:假设单个任务超时为 1000ms ,任务执行超过该时间发起报警。

    hippo4j 支持钉钉、企业微信和飞书软件通知,线程池任务运行超时报警示例:

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

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

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

    5. 线程池监控

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

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

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

    6. 整合 Spring ThreadPoolTaskExecutor

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

    1. 当服务停止时,通知线程池处理剩余任务,并在等待指定时间后强制停止。
    2. 传递线程上下文到线程池执行上下文中。

    第一个是实际使用中很核心的功能,减少了线程池丢弃任务的可能,这里重点说明下。

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

    可能执行完了,可能没有

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

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

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

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

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

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

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

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

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

    8. 线程池运行堆栈查看

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

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

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

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

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

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

    hippo4j 支持的两种运行模式

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

    1. hippo4j config

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

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

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

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

    2. hippo4j server

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

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

    1. 优点:功能齐备,可以享受更多的服务和便利。如果应用启动的是集群,可以指定其中某一个实例的线程池修改,而 config 则是整个集群变更。
    2. 不足:相比较 hippo4j config ,需要额外部署一个 jar 包,增加了部署工作量。

    如果最初使用 hippo4j config ,想要切换到 server ,两者在进行替换的时候,无需修改业务代码

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

    文末结语

    如果觉得有用,麻烦各位大佬在以下两个平台 star 支持下,非常感谢~

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

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

    24 条回复    2022-11-04 09:05:49 +08:00
    muchenlou
        1
    muchenlou  
       2022-11-03 09:08:31 +08:00   1
    今天才周四呀。
    bthulu
        2
    bthulu  
       2022-11-03 09:16:11 +08:00
    有没有考虑过把动态内存和动态 CPU 加上去呢? 可运行时修改 jvm 内存大小, 动态修改 jvm 内存地址数据, 动态变更 cpu 数量, 动态限制 cpu 频率, 动态更换 CPU
    dqzcwxb
        3
    dqzcwxb  
       2022-11-03 09:22:57 +08:00   1
    说个未来的点,不是针对你哈
    jdk19 的预览特性 virtual thread 已经不再推荐将线程池化(因为无论你创建多少个 virtual thread 底层都是一个 forkjoinpool 线程池在执行)
    virtual thread 应该是在 jdk21 正式推出并在这之前解决 LocalThread 和 Synchronized 问题

    最后,很感谢你的开源精神
    machen
        4
    machen  
    OP
       2022-11-03 09:29:46 +08:00
    @muchenlou 提前庆祝了 哈哈
    machen
        5
    machen  
    OP
       2022-11-03 09:30:35 +08:00
    @bthulu 想做,但是感觉很难,如果有好的思路欢迎分享
    machen
        6
    machen  
    OP
       2022-11-03 09:31:40 +08:00
    @dqzcwxb 15 年开始工作,那时候就是 java8 ,7 年过去了,依旧还是。不知道程序员生涯能不能看到大量公司切换到 19 ~
    bthulu
        7
    bthulu  
       2022-11-03 09:34:44 +08:00
    @dqzcwxb LocalThread 和 Synchronized 问题感觉 jdk21 前应该解决不了, jdk24 看看有么有可能
    kimera
        8
    kimera  
       2022-11-03 09:38:29 +08:00
    挺好的 学习一下
    dqzcwxb
        9
    dqzcwxb  
       2022-11-03 09:39:59 +08:00
    @bthulu LocalThread 的不是很难增加 virtual thread 相关判断即可
    Synchronized 就比较复杂了但是我觉得 21 应该能搞完,要不然 21 几乎没有吸引力放出来还不如不放
    @machen 但是人总要往前看,只关注过去是没有未来的
    leafre
        10
    leafre  
       2022-11-03 09:41:00 +08:00
    @dqzcwxb 国内 jdk19 根本不存在普及的可能,11 都推不开
    Createsequence
        11
    Createsequence  
       2022-11-03 10:01:33 +08:00
    已 start ,感谢楼主分享~
    chunworkhard
        12
    chunworkhard  
       2022-11-03 10:07:00 +08:00
    学习下~ 感谢楼主
    Weakred
        13
    Weakred  
       2022-11-03 10:27:22 +08:00
    star 已点,感谢楼主分享
    fkdog
        14
    fkdog  
       2022-11-03 10:49:00 +08:00
    v2 上一堆推广动态线程池的,讲道理感觉你这个和他们的比起来没有什么新意。
    machen
        15
    machen  
    OP
       2022-11-03 10:55:12 +08:00
    @fkdog 大佬觉得什么是有新意,可以说出来聊聊,吸取吸取意见
    fkdog
        16
    fkdog  
       2022-11-03 11:01:19 +08:00
    @machen
    非大佬。
    敲碗等一个引入机器学习来实现自动调配线程池参数的。
    superliy
        17
    superliy  
       2022-11-03 11:12:47 +08:00
    好像动态线程池的几个项目都没有监控 netty 线程池,为啥呢? spring reactive 用 netty 作为 web 服务时,怎么监控的
    weihubeatsjjjjj
        18
    weihubeatsjjjjj  
       2022-11-03 11:13:51 +08:00
    @fkdog 看了下还是有一些新意的,我总结一下看说的对不对
    1. 支持页面操作和监控线程池(相对后端框架有前端页面支持是比较少见的,其他动态线程池没有)
    2. 整体代码风格设计都还不错,特别是动态线程池的扩展是采用插件的形式,扩展性还是比较强
    3. 项目整体活跃度都是比较高的,有任何 bug 或新需求应该可以及时响应
    machen
        19
    machen  
    OP
       2022-11-03 11:20:09 +08:00
    @superliy 可以考虑,后续我研究下
    lysS
        20
    lysS  
       2022-11-03 12:25:07 +08:00
    @dqzcwxb 协程化比池化的好处在于,阻塞时,协程可以不阻塞线程
    VB1
        21
    VB1  
       2022-11-03 13:59:14 +08:00
    @dqzcwxb 请问下“LocalThread 和 Synchronized 问题”这个具体是什么问题,可以详细说一下么,或者有什么文章链接,不胜感激。
    dqzcwxb
        22
    dqzcwxb  
       2022-11-03 14:46:29 +08:00   2
    @VB1 LocalThread 应该是 ThreadLocal 手快打反了不好意思
    相关资料:https://developer.aliyun.com/article/1026412#slide-15
    machen
        23
    machen  
    OP
       2022-11-03 21:29:28 +08:00
    @superliy 了解了下,EventLoopGroup 对象包裹的线程组,一个对象一个 Event 里面是一个线程,应该不适用适配 Netty
    onlyxin
        24
    onlyxin  
       2022-11-04 09:05:49 +08:00
    看了下,还是很赞的一个项目,已 start 。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     971 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 19:30 PVG 03:30 LAX 12:30 JFK 15:30
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86