项目地址

gitee地址:gitee.com/yanhom/dyna…

github地址:github.com/lyh200/dyna…


系列文章

动态线程池框架(DynamicTp),监控及源码解析篇

动态线程池(DynamicTp)之动态调整Tomcat、Jetty、Undertow线程池参数篇

美团动态线程池实践思路开源项目(DynamicTp),线程池源码解析及通知告警篇


写在前面

稍微有些Java编程经验的小伙伴都知道,Java的精髓在juc包,这是大名鼎鼎的Doug Lea老爷 子的杰作,评价一个程序员Java水平怎么样,一定程度上看他对juc包下的一些技术掌握的怎么样,这也是面试中的基本上必问的一些技术点之一。

juc包主要包括:

1.原子类(AtomicXXX)

2.锁类(XXXLock)

3.线程同步类(AQS、CountDownLatch、CyclicBarrier、Semaphore、Exchanger)

4.任务执行器类(Executor体系类,包括今天的主角ThreadPoolExecutor)

5.并发集合类(ConcurrentXXX、CopyOnWriteXXX)相关集合类

6.阻塞队列类(BlockingQueue继承体系类)

7.Future相关类

8.其他一些辅助工具类

多线程编程场景下,这些类都是必备技能,会这些可以帮助我们写出高质量、高性能、少bug的代码,同时这些也是Java中比较难啃的一些技术,需要持之以恒,学以致用,在使用中感受他们带来的奥妙。

上边简单罗列了下juc包下功能分类,这篇文章我们主要来介绍动态可监控线程池的,所以具体内容也就不展开讲了,以后有时间单独来聊吧。看这篇文章前,希望读者最好有一定的线程池ThreadPoolExecutor使用经验,不然看起来会有点懵。

如果你对ThreadPoolExecutor不是很熟悉,推荐阅读下面两篇文章

javadoop: www.javadoop.com/post/java-t…

美团技术博客: tech.meituan.com/2020/04/02/…


背景

使用ThreadPoolExecutor过程中你是否有以下痛点呢?

1.代码中创建了一个ThreadPoolExecutor,但是不知道那几个核心参数设置多少比较合适

2.凭经验设置参数值,上线后发现需要调整,改代码重启服务,非常麻烦

3.线程池相对开发人员来说是个黑盒,运行情况不能感知到,直到出现问题

如果你有以上痛点,这篇文章要介绍的动态可监控线程池(DynamicTp)或许能帮助到你。

如果看过ThreadPoolExecutor的源码,大概可以知道其实它有提供一些set方法,可以在运行时动态去修改相应的值,这些方法有:

public void setCorePoolSize(int corePoolSize);
public void setMaximumPoolSize(int maximumPoolSize);
public void setKeepAliveTime(long time, TimeUnit unit);
public void setThreadFactory(ThreadFactory threadFactory);
public void setRejectedExecutionHandler(RejectedExecutionHandler handler);

现在大多数的互联网项目其实都会微服务化部署,有一套自己的服务治理体系,微服务组件中的分布式配置中心扮演的就是动态修改配置,实时生效的角色。那么我们是否可以结合配置中心来做运行时线程池参数的动态调整呢?答案是肯定的,而且配置中心相对都是高可用的,使用它也不用过于担心配置推送出现问题这类事儿,而且也能减少研发动态线程池组件的难度和工作量。

综上,我们总结出以下的背景

  • 广泛性:在Java开发中,想要提高系统性能,线程池已经是一个90%以上的人都会选择使用的基础工具
  • 不确定性:项目中可能会创建很多线程池,既有IO密集型的,也有CPU密集型的,但线程池的参数并不好确定;需要有套机制在运行过程中动态去调整参数
  • 无感知性,线程池运行过程中的各项指标一般感知不到;需要有套监控报警机制在事前、事中就能让开发人员感知到线程池的运行状况,及时处理
  • 高可用性,配置变更需要及时推送到客户端;需要有高可用的配置管理推送服务,配置中心是现在大多数互联网系统都会使用的组件,与之结合可以大幅度减少开发量及接入难度

简介

我们基于配置中心对线程池ThreadPoolExecutor做一些扩展,实现对运行中线程池参数的动态修改,实时生效;以及实时监控线程池的运行状态,触发设置的报警策略时报警,报警信息会推送办公平台(钉钉、企微等)。报警维度包括(队列容量、线程池活性、拒绝触发等);同时也会定时采集线程池指标数据供监控平台可视化使用。使我们能时刻感知到线程池的负载,根据情况及时调整,避免出现问题影响线上业务。

    |  __ \                            (_) |__   __|| |  | |_   _ _ __   __ _ _ __ ___  _  ___| |_ __  | |  | | | | | '_ \ / _` | '_ ` _ | |/ __| | '_ \ | |__| | |_| | | | | (_| | | | | | | | (__| | |_) ||_____/ __, |_| |_|__,_|_| |_| |_|_|___|_| .__/ __/ |                              | |    |___/                               |_|    :: Dynamic Thread Pool ::

特性

  • 参考美团线程池实践 ,对线程池参数动态化管理,增加监控、报警功能
  • 基于Spring框架,现只支持SpringBoot项目使用,轻量级,引入starter即可食用
  • 基于配置中心实现线程池参数动态调整,实时生效;集成主流配置中心,默认支持Nacos、Apollo,同时也提供SPI接口可自定义扩展实现
  • 内置通知报警功能,提供多种报警维度(配置变更通知、活性报警、容量阈值报警、拒绝策略触发报警),默认支持企业微信、钉钉报警,同时提供SPI接口可自定义扩展实现
  • 内置线程池指标采集功能,支持通过MicroMeter、JsonLog日志输出、Endpoint三种方式,可通过SPI接口自定义扩展实现
  • 集成管理常用第三方组件的线程池,已集成SpringBoot内置WebServer(Tomcat、Undertow、Jetty)的线程池管理

架构设计

主要分四大模块

  • 配置变更监听模块:

    1.监听特定配置中心的指定配置文件(默认实现Nacos、Apollo),可通过内部提供的SPI接口扩展其他实现

    2.解析配置文件内容,内置实现yml、properties配置文件的解析,可通过内部提供的SPI接口扩展其他实现

    3.通知线程池管理模块实现刷新

  • 线程池管理模块:

    1.服务启动时从配置中心拉取配置信息,生成线程池实例注册到内部线程池注册中心中

    2.监听模块监听到配置变更时,将变更信息传递给管理模块,实现线程池参数的刷新

    3.代码中通过getExecutor()方法根据线程池名称来获取线程池对象实例

  • 监控模块:

    实现监控指标采集以及输出,默认提供以下三种方式,也可通过内部提供的SPI接口扩展其他实现

    1.默认实现Json log输出到磁盘

    2.MicroMeter采集,引入MicroMeter相关依赖

    3.暴雷Endpoint端点,可通过http方式访问

  • 通知告警模块:

    对接办公平台,实现通告告警功能,默认实现钉钉、企微,可通过内部提供的SPI接口扩展其他实现,通知告警类型如下

    1.线程池参数变更通知

    2.阻塞队列容量达到设置阈值告警

    3.线程池活性达到设置阈值告警

    4.触发拒绝策略告警


使用

  • maven依赖
  1. apollo应用用接入用此依赖

        <dependency><groupId>io.github.lyh200</groupId><artifactId>dynamic-tp-spring-boot-starter-apollo</artifactId><version>1.0.0</version></dependency>
    
  2. spring-cloud场景下的nacos应用接入用此依赖
        <dependency><groupId>io.github.lyh200</groupId><artifactId>dynamic-tp-spring-cloud-starter-nacos</artifactId><version>1.0.0</version></dependency>
    
  3. 非spring-cloud场景下的nacos应用接入用此依赖
        <dependency><groupId>io.github.lyh200</groupId><artifactId>dynamic-tp-spring-boot-starter-nacos</artifactId><version>1.0.0</version></dependency>
    
  • 线程池配置

    spring:dynamic:tp:enabled: trueenabledBanner: true        # 是否开启banner打印,默认trueenabledCollect: false      # 是否开启监控指标采集,默认falsecollectorType: logging     # 监控数据采集器类型(JsonLog | MicroMeter),默认logginglogPath: /home/logs        # 监控日志数据路径,默认${user.home}/logsmonitorInterval: 5         # 监控时间间隔(报警判断、指标采集),默认5snacos:                     # nacos配置,不配置有默认值(规则name-dev.yml这样)dataId: dynamic-tp-demo-dev.ymlgroup: DEFAULT_GROUPapollo:                    # apollo配置,不配置默认拿apollo配置第一个namespacenamespace: dynamic-tp-demo-dev.ymlconfigType: yml            # 配置文件类型platforms:                 # 通知报警平台配置- platform: wechaturlKey: 3a7500-1287-4bd-a798-c5c3d8b69c  # 替换receivers: test1,test2                   # 接受人企微名称- platform: dingurlKey: f80dad441fcd655438f4a08dcd6a     # 替换secret: SECb5441fa6f375d5b9d21           # 替换,非sign模式可以没有此值receivers: 15810119805                   # 钉钉账号手机号    tomcatTp:                                    # tomcat web server线程池配置minSpare: 100max: 400      jettyTp:                                     # jetty web server线程池配置min: 100max: 400     undertowTp:                                  # undertow web server线程池配置ioThreads: 100workerThreads: 400      executors:                                   # 动态线程池配置- threadPoolName: dynamic-tp-test-1corePoolSize: 6maximumPoolSize: 8queueCapacity: 200queueType: VariableLinkedBlockingQueue   # 任务队列,查看源码QueueTypeEnum枚举类rejectedHandlerType: CallerRunsPolicy    # 拒绝策略,查看RejectedTypeEnum枚举类keepAliveTime: 50allowCoreThreadTimeOut: falsethreadNamePrefix: test           # 线程名前缀notifyItems:                     # 报警项,不配置自动会配置(变更通知、容量报警、活性报警、拒绝报警)- type: capacity               # 报警项类型,查看源码 NotifyTypeEnum枚举类enabled: truethreshold: 80                # 报警阈值platforms: [ding,wechat]     # 可选配置,不配置默认拿上层platforms配置的所以平台interval: 120                # 报警间隔(单位:s)- type: changeenabled: true- type: livenessenabled: truethreshold: 80- type: rejectenabled: truethreshold: 1
    
  • 代码方式生成,服务启动会自动注册

    @Configuration
    public class DtpConfig {@Beanpublic DtpExecutor demo1Executor() {return DtpCreator.createDynamicFast("demo1-executor");}@Beanpublic ThreadPoolExecutor demo2Executor() {return ThreadPoolBuilder.newBuilder().threadPoolName("demo2-executor").corePoolSize(8).maximumPoolSize(16).keepAliveTime(50).allowCoreThreadTimeOut(true).workQueue(QueueTypeEnum.SYNCHRONOUS_QUEUE.getName(), null, false).rejectedExecutionHandler(RejectedTypeEnum.CALLER_RUNS_POLICY.getName()).buildDynamic();}
    }
    
  • 代码调用,根据线程池名称获取

    public static void main(String[] args) {DtpExecutor dtpExecutor = DtpRegistry.getExecutor("dynamic-tp-test-1");dtpExecutor.execute(() -> System.out.println("test"));
    }
    

注意事项

  1. 配置文件配置的参数会覆盖通过代码生成方式配置的参数

  2. 阻塞队列只有VariableLinkedBlockingQueue类型可以修改capacity,该类型功能和LinkedBlockingQueue相似,只是capacity不是final类型,可以修改,

VariableLinkedBlockingQueue参考RabbitMq的实现

  1. 启动看到如下日志输出证明接入成功

    
    |  __ \                            (_) |__   __|
    | |  | |_   _ _ __   __ _ _ __ ___  _  ___| |_ __
    | |  | | | | | '_ \ / _` | '_ ` _ | |/ __| | '_ \
    | |__| | |_| | | | | (_| | | | | | | | (__| | |_) |
    |_____/ __, |_| |_|__,_|_| |_| |_|_|___|_| .__/ __/ |                              | |    |___/                               |_|    :: Dynamic Thread Pool :: DynamicTp register, executor: DtpMainPropWrapper(dtpName=dynamic-tp-test-1, corePoolSize=6, maxPoolSize=8, keepAliveTime=50, queueType=VariableLinkedBlockingQueue, queueCapacity=200, rejectType=RejectedCountableCallerRunsPolicy, allowCoreThreadTimeOut=false)
    
  2. 配置变更会推送通知消息,且会高亮变更的字段

    
    DynamicTp [dynamic-tp-test-2] refresh end, changed keys: [corePoolSize, queueCapacity], corePoolSize: [6 => 4], maxPoolSize: [8 => 8], queueType: [VariableLinkedBlockingQueue => VariableLinkedBlockingQueue], queueCapacity: [200 => 2000], keepAliveTime: [50s => 50s], rejectedType: [CallerRunsPolicy => CallerRunsPolicy], allowsCoreThreadTimeOut: [false => false]
    

通知报警

触发报警阈值会推送相应报警消息,且会高亮显示相关字段,活性告警、容量告警、拒绝告警

配置变更会推送通知消息,且会高亮变更的字段


监控日志

通过主配置文件collectType属性配置指标采集类型,默认值:logging

  • micrometer方式:通过引入micrometer相关依赖采集到相应的平台

(如Prometheus,InfluxDb...)

  • logging:指标数据以json格式输出日志到磁盘,地址logPath/dynamictp/{logPath}/ dynamictp/logPath/dynamictp/{appName}.monitor.log

    2022-01-16 15:25:20.599 INFO [dtp-monitor-thread-1:d.m.log] {"activeCount":2,"queueSize":100,"largestPoolSize":4,"poolSize":2,"rejectHandlerName":"CallerRunsPolicy","queueCapacity":1024,"fair":false,"rejectCount":0,"waitTaskCount":10,"taskCount":120,"queueRemainingCapacity":1024,"corePoolSize":6,"queueType":"VariableLinkedBlockingQueue","completedTaskCount":1078,"dtpName":"remoting-call","maximumPoolSize":8}
    2022-01-16 15:25:25.603 INFO [dtp-monitor-thread-1:d.m.log] {"activeCount":2,"queueSize":120,"largestPoolSize":4,"poolSize":2,"rejectHandlerName":"CallerRunsPolicy","queueCapacity":1024,"fair":false,"rejectCount":0,"waitTaskCount":20,"taskCount":140,"queueRemainingCapacity":1024,"corePoolSize":6,"queueType":"VariableLinkedBlockingQueue","completedTaskCount":1459,"dtpName":"remoting-call","maximumPoolSize":8}
    2022-01-16 15:25:30.609 INFO [dtp-monitor-thread-1:d.m.log] {"activeCount":2,"queueSize":140,"largestPoolSize":4,"poolSize":2,"rejectHandlerName":"CallerRunsPolicy","queueCapacity":1024,"fair":false,"rejectCount":0,"waitTaskCount":89,"taskCount":180,"queueRemainingCapacity":1024,"corePoolSize":6,"queueType":"VariableLinkedBlockingQueue","completedTaskCount":1890,"dtpName":"remoting-call","maximumPoolSize":8}
    2022-01-16 15:25:35.613 INFO [dtp-monitor-thread-1:d.m.log] {"activeCount":2,"queueSize":160,"largestPoolSize":4,"poolSize":2,"rejectHandlerName":"CallerRunsPolicy","queueCapacity":1024,"fair":false,"rejectCount":0,"waitTaskCount":99,"taskCount":230,"queueRemainingCapacity":1024,"corePoolSize":6,"queueType":"VariableLinkedBlockingQueue","completedTaskCount":2780,"dtpName":"remoting-call","maximumPoolSize":8}
    2022-01-16 15:25:40.616 INFO [dtp-monitor-thread-1:d.m.log] {"activeCount":2,"queueSize":230,"largestPoolSize":4,"poolSize":2,"rejectHandlerName":"CallerRunsPolicy","queueCapacity":1024,"fair":false,"rejectCount":0,"waitTaskCount":0,"taskCount":300,"queueRemainingCapacity":1024,"corePoolSize":6,"queueType":"VariableLinkedBlockingQueue","completedTaskCount":4030,"dtpName":"remoting-call","maximumPoolSize":8}
    
  • 暴露EndPoint端点(dynamic-tp),可以通过http方式请求

    [{"dtp_name": "remoting-call","core_pool_size": 8,"maximum_pool_size": 16,"queue_type": "SynchronousQueue","queue_capacity": 0,"queue_size": 0,"fair": false,"queue_remaining_capacity": 0,"active_count": 2,"task_count": 2760,"completed_task_count": 2760,"largest_pool_size": 16,"pool_size": 8,"wait_task_count": 0,"reject_count": 12462,"reject_handler_name": "CallerRunsPolicy"}
    ]
    

美团动态线程池实践思路已开源相关推荐

  1. 美团动态线程池实践思路,开源了

    大家好,今天我们来聊一个比较实用的话题,动态可监控的线程池实践,全新开源项目(DynamicTp)地址在文章末尾,欢迎交流学习. 写在前面 稍微有些Java编程经验的小伙伴都知道,Java的精髓在ju ...

  2. 美团动态线程池实践思路开源项目(DynamicTp),线程池源码解析及通知告警篇

    大家好,这篇文章我们来聊下动态线程池开源项目(DynamicTp)的通知告警模块.目前项目提供以下通知告警功能,每一个通知项都可以独立配置是否开启.告警阈值.告警间隔时间.平台等,具体代码请看core ...

  3. 今天我们来聊一个比较实用的话题,动态可监控的线程池实践,全新开源项目

    大家好,今天我们来聊一个比较实用的话题,动态可监控的线程池实践,全新开源项目(DynamicTp)地址在下方,欢迎star交流学习. 项目地址 gitee地址:gitee.com/yanhom/dyn ...

  4. 美团:某动态线程池框架是官方开源的么?

    大家好,我是马称. 最近,有很多同学在微信上问我这么一个问题: Hippo4j 动态线程池框架是美团开源的么? 类似于这样的问题还挺多,在这里统一回复下: 美团官方并没有开源任何关于动态线程池的框架. ...

  5. 美团动态线程池开源框架 DynamicTp

    背景 使用线程池 ThreadPoolExecutor 过程中你是否有以下痛点呢? 1.代码中创建了一个 ThreadPoolExecutor,但是不知道那几个核心参数设置多少比较合适 2.凭经验设置 ...

  6. 借鉴美团文章实现的动态线程池,已开源

    大家好, 我是龙台 给自己定了个小目标,hippo4j 年底达到 1k star! GitHub:https://github.com/acmenlt/dynamic-threadpool Site: ...

  7. 推荐一个强大的开源动态线程池项目

    今天和大家分享的是一个动态线程池,Hippo4J Hippo4J,是一个基于美团动态线程池的设计理念,针对该类线程池,在一般的基础上增强了动态调参.监控.报警等功能的一个版本. Hippo4J可以通过 ...

  8. Hippo4j和DynamicTp动态线程池介绍和使用中遇到的坑

    文章目录 @[toc] 1.前言 1.1Dromara致力于微服务云原生解决方案的组织 1.2 动态线程池的思路 1.3Hippo4j和DynamicTp动态线程池解决什么痛点 2.介绍 2.1Hip ...

  9. 美团简单版动态线程池源码实现

    背景 动态线程池,指的是线程池中的参数可以动态修改并生效,比如corePoolSize.maximumPoolSize等. 在工作中,线程池的核心线程数和最大线程数等参数是很难估计和固定的,如果能在应 ...

最新文章

  1. 黑客常用SQL注入绕过技术总结!
  2. ppt设置外观样式_如何设置更符合需要的幻灯片背景?
  3. drupal与html转换,Drupal 7修改網頁HTML和顯示Title的辦法
  4. TensorFlow Lite+Android,Google要搞的大事情
  5. 数学建模——灰色预测模型Python代码
  6. 让fieldset在td的顶端 解决fieldset在td中间的问题
  7. s7 200 java_java android 读写西门子PLC数据,包含S7协议和Fetch/Write协议,s7支持200smart,300PLC,1200PLC,1500PLC...
  8. mysql scrapy 重复数据_MySQL大数据量表中删除重复记录
  9. web.config SetAttributes
  10. 第十二章课下测试补交博客
  11. 快速完成单片机毕业设计方法
  12. 蒙特卡洛近似的一些例子
  13. js 线性最小二乘回归线方程
  14. HTML颜色码对照表-英文代码、中文描述、十六进制、rgb值
  15. 如何定期清理DNS缓存?清理DNS缓存有什么用?
  16. ETL: Extraction Transformation Loading
  17. Python和FFmpeg将语音记录转换成可共享的视频,非常炫酷。
  18. 成功解决raise TypeError(‘Unexpected feature_names type‘)TypeError: Unexpected feature_names type
  19. 努力构建15分钟听力圈,腾讯天籁行动助力听障老人更快融入数字社会
  20. 雀巢旗下巧克力品牌芭绮正式进入中国;国内首款工业级防水机器狗“绝影X20”亮相 | 美通社头条...

热门文章

  1. 荣耀9i升级android10,荣耀9i和荣耀10哪个好_荣耀9i和荣耀10评测对比_飞翔教程
  2. android badge,Android应用图标上的小红点Badge实现
  3. android 最新pdf下载,深入剖析Android系统 (杨长刚) 中文pdf扫描版[127MB]
  4. 数据科学工作中存在的7大问题与解决方案
  5. 2022-2027年中国构树行业市场调研及未来发展趋势预测报告
  6. cf489c Given Length and Sum of Digits...
  7. 工作日记之EasyExcel
  8. KMP经典超简约模板
  9. 关于微信分身,同时登陆两个微信
  10. 北京万博浩源小心车辆购置税退税骗子