乐趣区

关于后端:美团动态线程池开源框架-DynamicTp

背景应用线程池 ThreadPoolExecutor 过程中你是否有以下痛点呢?1. 代码中创立了一个 ThreadPoolExecutor,然而不晓得那几个外围参数设置多少比拟适合 2. 凭教训设置参数值,上线后发现须要调整,改代码从新公布服务,十分麻烦 3. 线程池绝对开发人员来说是个黑盒,运行状况不能及时感知到,直到呈现问题如果你有以上痛点,动静可监控线程池框架(DynamicTp)或者能帮忙到你。如果看过 ThreadPoolExecutor 的源码,大略能够晓得它对外围参数根本都有提供 set / get 办法以及一些扩大办法,能够在运行时动静批改、获取相应的值,这些办法有: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);
public void allowCoreThreadTimeOut(boolean value);

public int getCorePoolSize();
public int getMaximumPoolSize();
public long getKeepAliveTime(TimeUnit unit);
public BlockingQueue<Runnable> getQueue();
public RejectedExecutionHandler getRejectedExecutionHandler();
public boolean allowsCoreThreadTimeOut();

protected void beforeExecute(Thread t, Runnable r);
protected void afterExecute(Runnable r, Throwable t);
复制代码当初大多数的互联网我的项目其实都会微服务化部署,有一套本人的服务治理体系,微服务组件中的分布式配置核心表演的就是动静批改配置,实时失效的角色。那么咱们是否能够联合配置核心来做运行时线程池参数的动静调整呢?答案是必定的,而且配置核心绝对都是高可用的,应用它也不必过于放心配置推送呈现问题这类事儿,而且也能缩小研发动静线程池组件自身的难度和工作量。综上,能够总结出以下的背景广泛性:在 Java 开发中,想要进步零碎性能,线程池曾经是一个 90% 以上的人都会抉择应用的根底工具不确定性:我的项目中可能会创立很多线程池,既有 IO 密集型的,也有 CPU 密集型的,但线程池的参数并不好确定;须要有套机制在运行过程中动静去调整参数无感知性:线程池运行过程中的各项指标个别感知不到;须要有套监控报警机制在事先、事中就能让开发人员感知到线程池的运行状况,及时处理高可用性:配置变更须要及时推送到客户端,须要有高可用的配置管理推送服务,配置核心是当初大多数互联网零碎都会应用的组件,与之联合能够极大进步零碎可用性个性基于以上背景剖析,咱们对线程池 ThreadPoolExecutor 做一些扩大加强,次要实现以下指标 1. 实现对运行中线程池参数的动静批改,实时失效 2. 实时监控线程池的运行状态,触发设置的报警策略时报警,报警信息推送办公平台 3. 定时采集线程池指标数据,配合像 grafana 这种可视化监控平台做大盘监控通过多个版本的迭代,目前最新版本 v1.0.9 具备以下个性 ✅代码零侵入:所有配置都放在配置核心,对业务代码零侵入轻量简略:基于 SpringBoot 实现,引入 starter,接入只需简略 4 步就可实现,顺利 3 分钟搞定高可扩大:框架外围性能都提供 SPI 接口供用户自定义个性化实现(配置核心、配置文件解析、告诉告警、监控数据采集、工作包装等等)线上大规模利用:参考美团线程池实际,美团外部曾经有该实践成熟的利用教训多平台告诉报警:提供多种报警维度(配置变更告诉、活性报警、容量阈值报警、回绝触发报警、工作执行或期待超时报警),已反对企业微信、钉钉、飞书、邮件报警,同时提供 SPI 接口可自定义扩大实现监控:定时采集线程池指标数据,反对通过 MicroMeter、JsonLog 日志输入、Endpoint 三种形式,可通过 SPI 接口自定义扩大实现工作加强:提供工作包装性能,实现 TaskWrapper 接口即可,如 MdcTaskWrapper、TtlTaskWrapper、SwTraceTaskWrapper,能够反对线程池上下文信息传递兼容性:JUC 一般线程池和 Spring 中的 ThreadPoolTaskExecutor 也能够被框架监控,@Bean 定义时加 @DynamicTp 注解即可可靠性:框架提供的线程池实现 Spring 生命周期办法,能够在 Spring 容器敞开前尽可能多的解决队列中的工作多模式:参考 Tomcat 线程池提供了 IO 密集型场景应用的 EagerDtpExecutor 线程池反对多配置核心:基于支流配置核心实现线程池参数动静调整,实时失效,已反对 Nacos、Apollo、Zookeeper、Consul、Etcd,同时也提供 SPI 接口可自定义扩大实现中间件线程池治理:集成治理罕用第三方组件的线程池,已集成 Tomcat、Jetty、Undertow、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars 等组件的线程池治理(调参、监控报警)架构设计框架性能大体能够分为以下几个模块 1. 配置变更监听模块 2. 服务外部线程池治理模块 3. 三方组件线程池治理模块 4. 监控模块 5. 告诉告警模块代码构造

1.adapter 模块:次要是适配一些第三方组件的线程池治理,目前曾经实现的有 SpringBoot 内置的三大 web 容器(TTomcat、Jetty、Undertow)、Dubbo、RocketMq、Hystrix、Grpc、Motan、Okhttp3、Brpc、Tars 的线程池治理,后续会接入其余罕用组件的线程池治理。2.common 模块:次要是一些各个模板都会用到的类,解耦依赖,复用代码,大家日常开发中可能也常常会这样做。3.core 模块:该框架的外围代码都在这个模块里,包含动静调整参数,监控报警,以及串联整个我的项目流程都在此。4.example 模块:提供一个简略应用示例,不便使用者参照 5.extension 模块:放一些扩大性能实现,比方基于 redis 的流控扩大、邮件发送扩大、skywalking 上下文传递扩大等 6.logging 模块:用于配置框架外部日志的输入,目前次要用于输入线程池监控指标数据到指定文件 7.starter 模块:提供独立功能模块的依赖封装、主动配置等相干。配置变更监听模块 1. 监听特定配置核心的指定配置文件(已实现 Nacos、Apollo、Zookeeper、Consul、Etcd),可通过外部提供的 SPI 接口扩大其余实现 2. 解析配置文件内容,内置实现 yml、properties、json 配置文件的解析,可通过外部提供的 SPI 接口扩大其余实现 3. 告诉线程池治理模块实现参数的刷新服务外部线程池治理模块 1. 服务启动时从配置核心拉取配置,生成线程池实例注册到外部线程池注册核心以及 Spring 容器中 2. 承受配置监听模块的刷新事件,实现线程池参数的刷新 3. 代码中通过依赖注入(举荐)或者 DtpRegistry.getDtpExecutor() 办法依据线程池名称来获取线程池实例三方组件线程池治理 1. 服务启动获取第三方中间件的线程池,被框架治理起来 2. 承受参数刷新、指标收集、告诉报警事件,进行相应的解决监控模块实现监控指标采集以及输入,默认提供以下三种形式,也可通过外部提供的 SPI 接口扩大其余实现 1. 默认实现 JsonLog 输入到磁盘,能够本人采集解析日志,存储展现 2.MicroMeter 采集,引入 MicroMeter 相干依赖,裸露相干端点,采集指标数据,联合 Grafana 做监控大盘 3. 暴雷自定义 Endpoint 端点(dynamic-tp),可通过 http 形式实时拜访告诉告警模块对接办公平台,实现告诉告警性能,已反对钉钉、企微、飞书、邮件,可通过外部提供的 SPI 接口扩大其余实现,告诉告警类型如下 1. 线程池主要参数变更告诉 2. 阻塞队列容量达到设置的告警阈值 3. 线程池活性达到设置的告警阈值 4. 触发回绝策略告警,格局:A/B,A:该报警项前后两次报警区间累加数量,B:该报警项累计总数 5. 工作执行超时告警,格局:A/B,A:该报警项前后两次报警区间累加数量,B:该报警项累计总数 6. 工作期待超时告警,格局:A/B,A:该报警项前后两次报警区间累加数量,B:该报警项累计总数

接入应用应用步骤 1. 引入相应配置核心的依赖,具体见官网 maven 依赖 2. 配置核心配置线程池实例,配置示例见官网(给出的是全配置项,不必的能够删除)3. 启动类加 @EnableDynamicTp 注解 4. 应用 @Resource 或 @Autowired 进行依赖注入,或通过 DtpRegistry.getDtpExecutor(“name”) 获取 5. 通过以上 4 步就能够应用了,是不是感觉很简略啊服务启动看到有如下日志输入阐明接入胜利 | \ (_) | __|
| | | |_ | |
| | | | | | | ‘_ \ / | '_ | |/ _| | ‘ \
| |__| | |_| | | | | (_| | | | | | | | (__| | |_) |
|_____/ __, |_| |_|__,_|_| |_| |_|_|___|_| .__/

     __/ |                              | |
    |___/                               |_|

:: Dynamic Thread Pool ::

DynamicTp register dtpExecutor, source: beanPostProcessor, executor: DtpMainPropWrapper(dtpName=dynamic-tp-test-1, corePoolSize=6, maxPoolSize=8, keepAliveTime=50, queueType=VariableLinkedBlockingQueue, queueCapacity=200, rejectType=RejectedCountableCallerRunsPolicy, allowCoreThreadTimeOut=false)

DtpRegistry initialization end, remote dtpExecutors: [dtpExecutor1, dtpExecutor2], local dtpExecutors: [ioIntensiveExecutor], local commonExecutors: [commonExecutor]
复制代码我的项目地址目前累计 2.5k star,欢送理解应用。官网:dynamictp.cngitee 地址:gitee.com/dromara/dyn…github 地址:github.com/dromara/dyn…

退出移动版