当前位置: 首页 > 后端技术 > Java

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

时间:2023-04-02 00:12:40 Java

大家好,本文将介绍动态线程池框架(DynamicTp)的适配模块,在之前也有介绍文章,该模块主要用于适配部分第三方组件的线程池管理,使第三方组件内置的线程池也能享受到动态参数调整和监控告警等增强功能。DynamicTp项目地址目前有500多颗star,感谢大家的star,欢迎pr,业务后贡献开源gitee地址:https://gitee.com/yanhom/dynamic-tpgithub地址:https://github.com/lyh200/dynamic-tp系列文章美团动态线程池实践思路,开源动态线程池框架(DynamicTp),监控与源码分析篇适配器已连接组件适配器模块现已连接内置的三个WebServerSpringBoot(Tomcat、Jetty、Undertow的线程池管理)在实现层面也与核心模块解耦,利用spring的事件机制进行通知监听处理。可以看到,当两个监听器在配置中心监听配置变化时,我们项目内部线程池更新后会发出一个RefreshEvent事件。DtpWebRefreshListener监听事件后会更新对应的WebServer线程池参数。监控告警也是如此。当DtpMonitor中执行监控任务时,会释放一个CollectEvent事件。DtpWebCollectListener监听事件后,会收集对应WebServer的线程池指标数据。如果要管理第三方组件的线程池,首先要对这些组件有一定的了解,了解整个请求的一个处理过程,找到处理请求对应的线程池。这些线程池不一定是JUC包下的ThreadPoolExecutor。类,或者说是组件自己实现的线程池,但是基本原理是类似的。Tomcat、Jetty、Undertow都是这样。他们没有直接使用JUC提供的线程池,而是自己实现了一套,或者扩展了JUC的实现;翻遍源码找到对应的线程池后,再看看有没有暴露出来的公共方法供我们调用获取?如果没有,我们需要考虑通过反射来获取。Tomcat内部线程池的实现Tomcat内部线程池并没有直接使用JUC下的ThreadPoolExecutor,而是选择继承JUC下的Executor系统类,然后重写exe??cute()等方法。不同版本有差异。1.继承JUC的原生ThreadPoolExecutor(9.0.50及以下版本),并覆盖一些方法,主要是execute()和afterExecute()2.继承JUC的AbstractExecutorService(9.0.51及以上版本),代码基本是JUC的翻版Tomcat的ThreadPoolExecutor也相应地微调了execute()方法。注意Tomcat实现的线程池类名也叫ThreadPoolExecutor,和JUC下的一样。Tomcat的ThreadPoolExecutor类的execute()方法如下:publicvoidexecute(Runnablecommand,longtimeout,TimeUnitunit){submittedCount.incrementAndGet();尝试{super.execute(command);}catch(RejectedExecutionExceptionrx){if(super.getQueue()instanceofTaskQueue){finalTaskQueuequeue=(TaskQueue)super.getQueue();try{if(!queue.force(command,timeout,unit)){submittedCount.decrementAndGet();抛出新的RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));}}catch(InterruptedExceptionx){submittedCount.decrementAndGet();抛出新的RejectedExecutionException(x);}}else{submittedCount.decrementAndGet();抛出RX;}}}可以看出,他首先调用了父类的execute()方法,然后捕获了RejectedExecutionException异常,然后判断任务队列类型是否为TaskQueue,尝试将任务添加到任务队列中。如果添加失败,则证明队列已满,然后执行拒绝策略。这里submittedCount是一个原子变量。记录被提交到这个线程池但是还没有被执行。任务数(主要用在下面提到的TaskQueue队列的offer()方法中),为什么要这样设计呢?继续阅读!Tomcat定义了一个阻塞队列TaskQueue继承自LinkedBlockingQueue,主要重写了offer()方法@Overridepublicbooleanoffer(Runnableo){//如果(parent==null)returnsuper.offer(o),我们不能做任何检查;//我们已经用完了线程,只需将对象排队if(parent.getPoolSize()==parent.getMaximumPoolSize())returnsuper.offer(o);//我们有空闲线程,只需将其添加到队列中即可if(parent.getSubmittedCount()<=(parent.getPoolSize()))returnsuper.offer(o);//如果我们的线程少于创建新线程的最大强制if(parent.getPoolSize()0)startThread();}jettyline进程提供了公共获取方法,获取方法publicExecutordoGetTp(WebServerwebServer){JettyWebServerjettyWebServer=(JettyWebServer)webServer;返回jettyWebServer.getServer().getThreadPool();码头对于线程池的线程参数,可以在引入DynamicTp依赖后在配置文件中添加如下配置。参数名称也与SpringBoot提供的Properties配置类参数相同。配置文件的完整示例见项目readme介绍spring:dynamic:tp://其他配置项jettyTp:#jettyweb服务器线程池配置min:100#核心线程数max:400#最大线程数Undertow的内部线程池实现Undertow仍然被大量使用,因为它性能强劲,重量轻,wildfly(以前的Jboss)从8开始就使用Undertow作为其内部默认的WebServer。之前是Tomcat。了解Undertow的朋友应该知道,它的底层是基于XNIO框架(3.X之前),也是Jboss开发的一个优秀的基于javanio的网络框架。但是Undertow宣布从3.0开始,底层网络框架将切换到Netty。官方给出的理由是,说到网络编程,Netty已经是事实上的标准。使用Netty的好处远大于XNIO所能提供的,让我们一起期待3.0的发布吧,可惜三年前就公布了,至今没有动静。不知道是死了还是怎么的。说实话,变化还是蛮大的。让我们看看它什么时候发布。下面的介绍是基于Undertow2.x版本的,在Undertow内部,定义了一个线程池顶层接口TaskPool,它有如图所示的几种实现。实际上,这些实现类是组合在一起的。他们维护了一个JUCExecutor系统类或者Jboss提供的EnhancedQueueExecutor类(也继承了JUCExecutorService类)。执行过程可以自己分析。具体创建代码如下,根据是否是外部导入,如果是导入,则使用外部导入的类,如果不是,则根据参数设置在内部创建一个,具体使用JUC的ThreadPoolExecutor或者Jboss的EnhancedQueueExecutor,根据配置参数选择Undertow线程池,没有提供public访问方式,所以通过反射获取,获取方式如下字段undertowField=ReflectionUtils.findField(UndertowWebServer.class,"undertow");如果(Objects.isNullder(undertowField)){返回null;}ReflectionUtils.makeAccessible(undertowField);Undertowundertow=(Undertow)ReflectionUtils.getField(undertowField,undertowWebServer);如果(Objects.isNull(undertow)){返回null;}返回undertow.getWorker();}如果想动态调整Undertow线程池的线程参数,可以在引入DynamicTp依赖后在配置文件中添加如下配置。配置文件的完整示例见项目readme介绍spring:dynamic:tp://其他配置项undertowTp:#undertowweb服务器线程池配置coreWorkerThreads:100#worker核心线程数maxWorkerThreads:400#worker最大线程数workerKeepAlive:60#空闲线程超时总结以上介绍了Tomcat、Jetty和Undertow的情况,重点介绍Tomcat,篇幅有限,其他两个有兴趣的可以自己分析,原理类似,也介绍了如何基于DynamicTp动态调整线程池的参数。我们在做WebServer性能调优的时候,可以动态调整参数。它真的很容易使用。再次欢迎大家使用DynamicTp框架共同完善项目。下一篇打算分享一个奇怪的问题,在使用DynamicTp的过程中,由于Tomcat版本不一致导致监控线程挂掉。通过一道题掌握ScheduledExecutorService的原理,欢迎大家继续关注。联系我欢迎加我微信或关注公众号一起交流,一起变强!公众号:CodeFox微信:yanhom1314