0。持续集成是可能的。同时,Jenkins提供了大量的各种插件来满足用户持续集成的需求。比如Jenkins提供的influxdb插件,可以将构建执行步骤、耗时、结果等数据发送到influxdb数据库,方便后期对构建数据进行分析和展示。Jenkins广泛应用于公司内部各类项目的持续集成,支持3000+项目,每天近万次构建。Jenkins是CI/CD的核心环节和重要环节,保证Jenkins的高可用和高性能显得尤为重要。一、症状我们的Jenkins服务运行一段时间后会异常卡死,严重降低持续集成速度,影响研发工作效率。问题出现后,我们第一时间查看了Jenkins监控仪表盘。从监控dashboard可以看到JVM线程数猛增,达到20K:2.问题分析2.1Dumpthreadstack发现问题后,上Jenkins机器,dumpjvm的threadstack。#获取Java进程idjps-l19768/home/maintain/jenkins-bin/jenkins/jenkins.war#dumpthreadstackjstack19768>jstack.txt2.2analyzethreadstack得到这个dumped的线程栈,我们使用https://fastthread.io/这个网站,分析jvm线程栈。大致结果如下:TotalThreadscount:20215ThreadGroup:RxNewThreadScheduler18600threads从上面的信息我们可以知道jvm一共有20215个线程,其中18600个是线程组RxNewThreadScheduler创建的线程。2.3定位线程的来源在JVM的线程栈中,出现了大量的RxNewThreadScheduler线程组。从字面上看,猜测应该是RxJava相关的线程。为了验证这个猜测,我们决定去查一下RxJava框架的源码,看看RxNewThreadScheduler线程是不是由RxJava框架生成的。在GitHub上rxjava的源码中搜索RxNewThreadScheduler,如下:代码:https://github.com/ReactiveX/...结果:确实,RxJava项目中包含一个线程池,其线程名前缀为RxNewThreadScheduler,并且代码在NewThreadScheduler类中,证实了我们的猜测。3、解决方案3.1排查思路经验证RxNewThreadScheduler线程名属于RxJava,极有可能是RxJava导致线程数暴涨的问题。问题是RxJava与Jenkins有什么关系?Jenkins的一个插件是不是引入了RxJava?这个问题好像没办法排查:我们的jenkins里面装了几十个插件,一个一个看源码不仅费时费力,而且还不一定能行:Jenkins插件的源码不一定直接写引用RxJava。我们只知道一个线程名称和它所属的应用程序RxJava。如何定位这个问题是从哪里引入的呢?从线程的dump信息来看,基本没有价值:"RxNewThreadScheduler-2"#4079daemonprio=5os_prio=0tid=0x00007fa2402a1000nid=0x5eafwaitingoncondition[0x00007fa12a9ae000]java.lang.Thread.State:TIMED_WAITING(parking)在sun.misc.Unsafe.park(NativeMethod)-在java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)在java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)在java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:10793).ScheduledThreadDelayPoolExecutor($ScheduledThreadPoolExecutor.java:809)在java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)在java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)atjava.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)atjava.lang.Thread.run(Thread.java:748)故障排除之路似乎be不能继续:疑惑无路,换位思考。既然问题是RxJava引入的,那我们看看Jenkins是怎么把这个RxJava加载进去的呢?毕竟RxJava的相关代码最终会运行在Jenkins对应的JVM中。有没有什么工具可以方便直观的查看JVM加载的类和jar包信息?Arthas提供了方便快捷的工具。3.2Arthas简介引用Arthas官网https://arthas.aliyun.com/doc...的介绍:Arthas是阿里巴巴开源的Java诊断工具,非常受开发者欢迎。Arthas可以帮助解决以下问题:这个类是从哪个jar加载的?如果遇到问题不能在线调试,难道只能加日志重新发布吗?如何直接从JVM中找到一个类的实例?当然,arthas能解决的不仅仅是上述问题。有关详细信息,请参阅官方文档。这里的第一个问题正是我们遇到的问题。我们需要知道是哪个jar包加载了RxJava相关的类。3.3解决方案——ArthasClassloader我们使用arthas来帮助排查问题(官方文档中有arthas的安装方法,这里不再赘述)。Arthas提供了查看类加载相关信息的功能:classloader-l。java-jararthas-boot.jarclassloader-l|tee/home/shared/log/arthas.log从arthas的输出中找到了RxJava:可以看到,RxJava是由influxdb插件引入的。注:引入influxdb是为Jenkins构建数据统计。没想到这个陷阱。考虑改用prometheus来收集数据。此时的感觉是:柳暗花明。3.4问题解决知道问题是influxdb插件引入后,我们先禁用influxdb插件,重启Jenkins。稳定运行一段时间后,观察Jenkins的线程数:可以看到Jenkins的线程数稳定在1K左右,没有暴涨。同时查看Jenkins任务的构建状态,已经恢复到正常水平,没有任何滞后和延迟。4.源码及根因分析Jenkins中引入了influxdb插件,用于存储和分析Jenkins构建的作业数据。为什么influxdb插件会导致Jenkins线程数飙升?这个问题的根本原因取决于插件的源代码。4.1Influxdb上报统计数据JenkinsJob构建时,influxdb插件会通过HTTP请求将统计数据存储在influxdb数据库中。Influxdb插件使用OkHttp+RxJava完成HTTP请求。下面将分析influxdb插件向influxdb数据库上报统计数据的关键流程源码:Jenkins每次build完成后,influxdb插件会调用writeToInflux方法上报相应的数据,如图下图:获取influxdb写的api,并写入通过api发送统计数据的关键是写API的配置:WriteOptions.DEFAULTS。我们来看看它的具体配置:关键是RxJava中提供的I/O线程调度器Scheduler。它的实现是Schedulers.newThread(),对应的代码如下:在Schedulers.newThread()方法中,看到了RxJava的身影,真正的处理逻辑交给newThreadScheduler处理:在初始化newThreadScheduler,创建一个NewThreadTask,真正的线程Handle逻辑交给他。4.2NewThreadScheduler调度器线程模型先来看NewThreadTask的定义:staticfinalclassNewThreadHolder{staticfinalSchedulerDEFAULT=newNewThreadScheduler();}staticfinalclassNewThreadTaskimplementsCallable
