V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
machen
V2EX  ›  Java

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

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

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

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

    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   ·   实用小工具   ·   5635 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 07:10 · PVG 15:10 · LAX 23:10 · JFK 02:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.