大家好,我是龙泰。一、前言Hippo4j的初衷是为了尽可能完善和保证线程池对线上应用的效果,所以加入了很多个性化的功能,间接导致了对Hippo4jServer项目的强烈依赖。自从Hippo4j1.0.0发布以来,社区小伙伴一直在问同一个问题,动态线程池如何轻装上阵?不,它来了。GitHub:https://github.com/acmenlt/dy...Gitee:https://gitee.com/acmenlt/dyn...随着Hippo4j1.1.0的发布,除了原有函数中的迭代输出,增加了一种使用模式:依托配置中心实现的轻量级动态线程池,将Hippo4j的源码从一种使用模式拆分为两种。两种模式共享一套核心源码,保留了大家关心的基本功能。该模块名为:Hippo4jCore。2、Hippo4jCore所谓“一图胜千言”,小编画了一张图来描述它的交互行为和支持的功能。只要你的项目中有配置中心,引用hippo4j-core-spring-boot-starter,就可以使用以上功能。1、动态线程池参数更新客户端项目启动时,向配置中心请求动态线程池配置,获取配置后创建DynamicThreadPool线程池。并向配置中心发起监控事件。当配置中心的配置发生变化时,监听事件实时修改项目中的线程池参数。如果在配置中心更改了动态线程池配置,日志中会打印更改信息:[MESSAGE-CONSUME]Changedthreadpool。coreSize::[1=>10]maxSize::[1=>20]queueType::[ResizableCapacityLinkedBlockIngQueue=>ResizableCapacityLinkedBlockIngQueue]容量::[1024=>2048]keepAliveTime::[1000=>1000]executeTimeOut::[600=>600]rejectedType::[DiscardOldestPolicy=>DiscardOldestPolicy]freadse=:allowCore>同时通过消息推送通知相关负责人。目前通知平台支持钉钉、企业微信、飞书三大常用办公软件。以企业微信群聊机器人为例:2、Web线程池参数更新SpringBoot内置了三个Web容器:Tomcat、Jetty、Undertow。Hippo4jCore已经支持更改容器线程池的核心参数:corePoolSize、maximumPoolSize、keepAliveTime。为什么要在Web线程池中加入动态更新?原因二:在对应用进行压测时,需要针对不同的压测流量调整web容器线程池的线程数。在正常流程中,调整后重新发布项目无疑是费时费力的;当SpringBootJava应用的响应时间变慢,服务器整体负载不高时,我们可以通过修改Web容器的线程池来提高并行处理能力,从而改善响应时间。当然,正常情况下,线上的容器线程池配置是压测后得到的最优值。所以这个功能在线上要慎用,或者尽量不要在线上使用。3、动态线程池告警策略为了让线程池运行出现问题,及时通知相关负责人,Hippo4j为线程池做了四种自定义的告警策略:活动告警:假设线程池活动告警阈值为设置为80%,最大线程数为10。当线程数达到8时,发起告警;阻塞队列容量告警:假设容量告警阈值设置为80%,阻塞队列容量为100,当容量达到80时,发出告警;任务拒绝告警:当线程池无法执行任务,开始执行拒绝策略时,发出告警;执行时间报警:假设线程池超时设置为1000ms,任务执行时间超过1000ms,则发出报警。问问题多的朋友,如果线程池频繁拒绝任务或者执行时间频繁超时,那岂不是要被信息轰炸了?将不会。优化了告警策略。设置告警间隔时,只会从线程池+告警类型两个维度发送一条通知告警信息。比如有一个线程池,线程池ID:message-consum,设置报警间隔为5分钟。也就是说,消息消费线程池在活跃度、阻塞队列容量、被拒绝的任务、执行时间等方面,最多可以在5分钟内发出每种类型的告警通知。目前支持钉钉、企业微信和飞书的群机器人报警。一个企业微信机器人的例子如下:上图中的链接信息只有在超时告警时才会存在,这样可以通过链接信息更方便的定位线程池任务执行慢的原因。3.代码示例Nacos或Apollo配置中心任选其一。SpringBootPom文件引入了Hippo4jCoreMaven坐标。cn.hippo4jhippo4j-core-spring-boot-starter1.1.0启动类添加@EnableDynamicThreadPool注解.@SpringBootApplication@EnableDynamicThreadPoolpublicclassExampleApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ExampleApplication.class,args);}}在配置中心添加spring.dynamic.thread-pool前缀的配置。如下:server:port:8090servlet:context-path:/examplespring:profiles:active:devdynamic:thread-pool:enable:true#是否启用动态线程池banner:true#是否打印bannercollect:true#是否开启线程池数据采集,对接Prometheuscheck-state-interval:3#检查线程池状态,是否满足报警条件,秒级notify-platforms:#通知报警平台,支持multiple,orchooseone-platform:'WECHAT'#企业微信secret-key:1d307bfa-815f-4662-a2e5-99415e947bb8-platform:'DING'#钉钉secret-key:56417ebba6a27ca352f0de77a2ae9da66d01f39610b5ee8a6033c60ef9071c55-platform:'LARK'#飞书secret-key:2cbf2808-3839-4c26-a04d-fd201dd51f9enacos:#选择其中一个nacosapollodata-id:xxxgroup:xxxapollo:namespace:xxxxconfig-file-type:yml#配置中心文件格式executors:-thread-pool-id:'message-consume'#线程池IDcore-pool-size:1#核心线程数maximum-pool-size:1#最大线程数queue-capacity:1#阻塞队列大小execute-time-out:1000#执行超时时间,任务执行时间超过该时间会触发报警blocking-queue:'LinkedBlockingQueue'#阻塞队列名,参考QueueTypeEnum,支持SPIrejected-handler:'AbortPolicy'#拒绝策略名称,参考RejectedPolicies,支持SPIkeep-alive-time:1024#线程存活时间,单位秒allow-core-thread-time-out:true#是否允许核心线程超时thread-name-prefix:'message-consume'#线程名前缀notify:#通知配置is-alarm:true#是否报警active-alarm:80#活动报警阈值;假设线程池最大线程数为10,当线程数达到8时,会发起告警capacity-alarm:80#容量告警阈值;假设阻塞队列的容量为100,当容量达到80时,发起一次告警interval:8#告警间隔,同一个线程池下的同一个告警纬度,interval时间内只发出一次告警,单位分钟收到:#选一个DING:'xxx'#手机WECHAT:'xxx'#填写企业微信用户号(填写其他不会达到@的效果)LARK:'xxx'#填写以开头的唯一用户号ou_,不然只能用普通的@使用Hippo4jThreadPoolBuilder构建动态线程池导入cn.hippo4j.core.executor.DynamicThreadPool;导入cn.hippo4j.core.executor.support.ThreadPoolBuilder;@Bean@DynamicThreadPoolpublicThreadPoolExecutordynamicThreadPoolExecutor(){StringconsumeThreadPoolId="message-consume.buthreadFactory";returnThreadPool(Builder)(consumeThreadPoolId).dynamicPool().build();}根据SpringBean注入方式使用动态线程池。@ResourceprivateThreadPoolExecutor动态线程池执行器;dynamicThreadPoolExecutor.execute(()->xxx);别了,还不容易吗?我粗略地试了一下,不到两分钟,你就可以快速的将你的SpringBoot项目连接到动态线程池中。总结一下接入步骤:在Pom中引入Hippo4jCore包依赖;为启动类添加动态线程池启用注解;在配置中心(Nacos或Apollo)添加动态线程池配置;在项目中以SpringBeans的形式创建动态线程池。四、常见问题1、项目关闭时,如何保证线程池中的所有任务都完成答:参考Spring封装的线程池框架。构建构建构建,指定;returnThreadPoolBuilder.builder().threadFactory(consumeThreadPoolId).waitForTasksToCompleteOnShutdown(true).awaitTerminationMillis(5000L).dynamicPool().build();}这两个参数是什么意思?waitForTasksToCompleteOnShutdown:关闭线程池时是否等待任务完成,这里我们设置为true;awaitTerminationMillis:等待任务完成的时间,单位毫秒。有很多疑问的朋友可能会问:为什么会有awaitTerminationMillis这个参数?只需等待所有任务完成即可。线程池中充满快速执行的任务可能没有问题。但是如果线程池里塞满了耗时任务怎么办?停止项目时等待几分钟或更长时间是无法忍受的。这个需要根据你项目的实际情况来评估。2.动态线程池是否可以传递上下文参数是可以的,也是基于Spring线程池框架。实现TaskDecorator接口,在构建动态线程池时指定。导入cn.hippo4j.core.executor.DynamicThreadPool;导入cn.hippo4j.core.executor.support.ThreadPoolBuilder;@Bean@DynamicThreadPoolpublicThreadPoolExecutordynamicThreadPoolExecutor(){StringconsumeThreadPoolId="message-consume.buthreadFactory";returnThreadPool(Builder)(consumeThreadPoolId).waitForTasksToCompleteOnShutdown(true).awaitTerminationMillis(5000L).taskDecorator(newTaskDecoratorTest.ContextCopyingDecorator()).dynamicPool().build();}3.Hippo4jCore和Hippo4j之间转换麻烦吗服务器?core,感觉功能不够用,想用Hippo4jServer,请问如何转换?这里其实很简单。从项目的角度来看:代码不需要做任何改动,只需要更改pom文件中的依赖坐标即可。二是将配置中心的配置迁移到Hippo4j控制台,在出来之前创建线程池记录。五、文末小结本文介绍了Hippo4j的一种新的使用模式:依赖配置中心的轻量级动态线程池的实现。Hippo4jServer和Hippo4jCore的质量不好评价。一个更强大,另一个是引入更轻的重量。至于如何使用,留给用户自行评估。如果项目中使用了配置中心和线程池,强烈建议大家在项目中引入试试看,这将为项目的在线稳定性提供额外的保障。由于个人能力有限,项目中难免会有无法考虑或需要优化的地方。各位小伙伴有兴趣提交PR进行修复。如果文章对你有帮助,请关注,不要白嫖我。本文由博客多发平台OpenWrite发布!