从源代码出发,Jenkins 任务排队时间过长问题的解决过程

最近开发了一个部署相关的工具,使用 Jenkins 来构建应用。Jenkins 的任务从模板中创建而来。每次部署时,通过 Jenkins API 来触发构建任务。在线上运行时发现,通过 API 触发的 Jenkins 任务总是会时不时在队列中等待较长的时间。某些情况下的等待时间甚至长达几分钟。直接在 Jenkins 界面上触发的任务却几乎不需要排队,直接马上就可以执行。过长的等待时间影响了构建的效率,这是一个急需解决的问题。这个问题奇怪的地方在于,手动从界面上触发的任务几乎不需要排队,而 API 触发的任务的排队时间则完全随机,毫无规律可言。

494f9a6e5f9311d241be67ed537ad5df.jpeg

当任务在队列中时,Jenkins 会在界面上显示该任务在队列中等待的原因。对于 API 创建的任务,它们的等待原因是“Finished waiting”。从这个原因的字面含义确实看不出来什么。当出现这样的问题时,最直接的办法是从源代码中寻找答案。

从 GitHub 上把 Jenkins 的源代码下载到本地。寻找问题的起点是搜索字符串“Finished waiting”。这个字符串定义在资源包(resource bundle)中,对应的键是 Queue.FinishedWaiting。再搜索使用了这个键的代码,定位到了类 hudson.model.Queue。从名字可以看出来,这个类表示的是 Jenkins 内部的工作队列。在这个类中定义了可能出现在队列中的不同类型的条目。类 WaitingItem 表示的是处于等待状态的条目。这个类的 getCauseOfBlockage()方法刚好用到了Queue.FinishedWaiting 这个键,表示当前条目被阻塞的原因。这个 WaitingItem 类是主要的调查目标。

WaitingItem 类中有 enter() 和 leave() 两个方法,分别表示该条目进入队列和离开队列。看起来那些等待时间过长的任务,在进入了队列之后,经过很长一段时间,它们的 leave() 方法才被调用。

b39845fd0bd5a44deba4da73ba648e43.jpeg

继续追踪这两个方法的调用,会发现 enter() 方法在 scheduleInternal() 中被调用,用来调度新的任务。而 leave() 方法则在 maintain() 中被调用,用来在合适的时机从队列中移除任务并执行。所以看起来问题是出在 maintain() 方法中。

maintain() 方法负责维护队列,并把任务在不同的状态中移动。当有可能影响到任务调度的情况发生时,Jenkins 会在内部调用这个方法。在 scheduleInternal() 方法中,把新的任务添加到队列之后,它会调用 scheduleMaintenance() 方法提交一个 Runnable 任务来调用 maintain() 方法。

3f73c17367fecdfb82a2797b1bf69815.jpeg

经过上面的分析,任务等待时间过长的原因可能是,maintain() 方法被调用时,并没有发现处于等待的任务。所以这个任务需要等到下一次 maintain() 方法调用时才会被执行。这中间可能有与时序相关的问题。


问题找到了之后,下一步考虑的是怎么解决问题。如果要从根本上解决问题,应该从 Jenkins 入手,尝试在 Jenkins 中找到问题发生的根源,并进行修复。这种方案要求对 Jenkins 的代码库有足够程度的了解,在本地构建开发环境,并尝试稳定地复现问题。找到问题并解决之后,还需要添加 Pull Request 到 Jenkins 的代码库,并等待新版本的发布。整个过程耗时漫长。作为 Jenkins 的使用者,我自己并没有太大的意愿去花费精力在 Jenkins 自身的问题上。这种与时序有关的问题,很难复现和调试。我需要的是一个能够有效解决问题的 workaround。

前面提到了,产生问题的原因是 maintain() 方法在执行时可能没有发现等待中的任务。那么解决的办法可以很直接,那就是每次提交任务之后,等待几秒钟,再调用一次 maintain() 方法。这样就可以确保 maintain() 方法能够发现等待中的任务。maintain() 方法是由 scheduleMaintenance() 方法调用的,而 scheduleMaintenance() 是 Queue 类的一个公开方法。我只需要能够调用这个 scheduleMaintenance() 方法就可以了。

Jenkins 有一个叫做脚本控制台的功能,可以在运行的 Jenkins 实例上执行 Groovy 脚本。Groovy 脚本可以直接对运行的 Jenkins 实例进行修改。

02161d8fbd8bfb7a26137f7b158b37ea.jpeg

每个 Jenkins 运行的实例中,Queue 对象只有一个。只需要找到这个 Queue 对象,并调用其中的 scheduleMaintenance() 方法,问题就解决了。脚本控制台已经内置提供了很多 Jenkins 的对象。实际上需要执行的代码很简单。

Jenkins.instance.queue.scheduleMaintenance()

通过在脚本控制台进行测试,发现只要执行了上述的脚本,原本在队列中的任务,马上就可以被调度执行。这也证明了问题确实解决了。

最后一个问题是如何把对 scheduleMaintenance() 的调用自动化,也就是在每次通过 API 触发了任务,等待几秒钟之后,自动调用 scheduleMaintenance() 方法。Jenkins 的脚本控制台并没有提供公开的 API。我采取的做法是用 HTTP 客户端模拟脚本控制台界面上的操作。脚本控制台的界面在运行脚本时,实际上执行了一个表单提交动作。这个表单被提交到了网址 <jenkins_url>/computer/(built-in)/script,内容类型是 application/x-www-form-urlencoded。请求中只需要包含一个参数 script,表示需要执行的脚本内容。Jenkins 使用的是 BASIC 认证,需要把访问的用户名和密码包含在请求中。使用 HTTP 客户端模拟上述请求并不难。

下面给出了使用 Spring RestTemplate 执行 Groovy 脚本的代码示例。

var headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
var authentication = "Basic " + Base64.getEncoder().encodeToString(String.format("%s:%s", username, password).getBytes(StandardCharsets.UTF_8));
headers.add("Authorization", authentication);
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("script", "Jenkins.instance.queue.scheduleMaintenance()");
var entity = new HttpEntity<>(map, headers);
try {restTemplate.exchange(String.format("%s/computer/(built-in)/script", jenkinsUrl),HttpMethod.POST, entity,String.class);
} catch (Exception e) {// 处理异常
}

至此,Jenkins 任务排队时间过长的问题得到了解决。虽然并没有从根本上解决问题,但已经是一个在有限的时间内可以完成的不错的解法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/182955.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Node.js案例 - 记账本

目录 项目效果 项目的搭建 ​编辑 响应静态网页 ​编辑 ​编辑 结合MongoDB数据库 结合API接口 进行会话控制 项目效果 该案例实现账单的添加删除查看&#xff0c;用户的登录注册。功能比较简单&#xff0c;但是案例主要是使用前段时间学习的知识进行实现的&#xff0c…

C++ AVL 树

AVL树的概念 当数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;此时二叉搜索树的搜索效率低下 解决方法&#xff1a;AVL树&#xff08;降低树的高度&#xff0c;从而减少平均搜索长度) 一棵AVL树或者是空树&#xff0c;或者是具有以下性质的二叉搜索树&#xff1…

JavaScript基础—函数、参数、返回值、作用域、变量、匿名函数、综合案例—转换时间,逻辑中断,转换为Boolean型

版本说明 当前版本号[20231129]。 版本修改说明20231126初版20231129完善部分内容 目录 文章目录 版本说明目录JavaScript 基础 - 第4天笔记函数声明和调用声明&#xff08;定义&#xff09;调用细节补充 参数形参和实参函数默认值 返回值作用域全局作用域局部作用域 变量全…

laraval6.0 GatewayWorker 交互通信

laravel 6.0 GatewayWorker 通讯 开发前准备下载 GatewayWorker 及操作方式前端demo测试效果项目中安装GatewayClient 开发前准备 GatewayClient 官网&#xff1a;https://www.workerman.net/ 当前使用的是宝塔操作 下载 GatewayWorker 及操作方式 前端demo 测试效果 项目中安…

纹理烘焙:原理及实现

纹理烘焙是计算机图形学中常见的技术&#xff0c;用于将着色器的细节传输到纹理中。 如果你的着色器计算量很大&#xff0c;但会产生静态结果&#xff0c;例如&#xff0c;这非常有用。 复杂的噪音。 NSDT在线工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器…

Ajax的使用方法

1,什么是Ajax&#xff1f; Ajax&#xff08;异步Javascript和XML&#xff09;&#xff0c;是指一种创建交互式网页应用的网页开发技术。 2&#xff0c;Ajax的作用 Ajax可以使网页实现异步更新----即在不更新整个页面的情况下实现对某一部分进行更新。 简单来说Ajax就是用于连接…

【Python】yaml.safe_load()函数详解和示例

在Python中&#xff0c;PyYAML库提供了对YAML&#xff08;YAML Ain’t Markup Language&#xff09;文件的强大支持。YAML是一种直观的数据序列化标准&#xff0c;可以方便地存储和加载配置文件、数据日志等。 yaml.safe_load和yaml.load是Python的PyYAML库提供的两个函数&…

从零搭建AlibabaCloud微服务项目

1&#xff0c;创建maven项目工程如下 equipment-admin 后台equipment-applet 前台或小程序端或app、h5equipment-common 公共模块equipment-gateway 网关equipment-mapper mapper层操作数据库equipment-model 实体类对应数据库表 2&#xff0c;在父pom文件引入依赖 <proper…

基于Java SSM框架实现美食推荐管理系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现美食推荐管理系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&a…

国内首个农业开源鸿蒙操作系统联合华为正式发布

2023年11月29日&#xff0c;在中国国际供应链促进博览会上&#xff0c;中信农业科技股份有限公司&#xff08;简称“中信农业”&#xff09;与深圳开鸿数字产业发展有限公司&#xff08;简称“深开鸿”&#xff09;以及华为技术有限公司&#xff08;简称“华为”&#xff09;联…

UniWebView 版本3 版本4 版本5介绍

一、介绍 UniWebView是iOS/Android上的web视图组件的包装器&#xff0c;所以运行时拥有与原生web相似性能。是针对Unity所写的插件&#xff0c;节省了项目的开发时间。 官网地址&#xff1a;UniWebView 二、下载&使用 1、下载 &#xff08;1&#xff09;、Unity Asset …

GAN:PacGAN-生成对抗网络中两个样本的威力

论文&#xff1a;https://arxiv.org/pdf/1712.04086.pdf 代码&#xff1a;GitHub - fjxmlzn/PacGAN: [NeurIPS 2018] [JSAIT] PacGAN: The power of two samples in generative adversarial networks 发表&#xff1a;2016 一、摘要 1&#xff1a;GAN最重大的缺陷是&#xf…

自己动手写 chatgpt: Attention 机制的原理与实现

chatgpt等大模型之所以成功都有赖于一种算法突破&#xff0c;那就是 attention 机制。这种机制能让神经网络更有效的从语言中抽取识别其内含的规律&#xff0c;同时它支持多路并行运算&#xff0c;因此相比于原来的自然语言处理算法&#xff0c;它能够通过并发的方式将训练的速…

leetcode 11. 盛最多水的容器(优质解法)

代码&#xff1a; class Solution {public int maxArea(int[] height) {int nheight.length;int left0;int rightn-1;int max0;while (left<right){//计算当前 left 和 right 所在位置的面积int areaMath.min(height[left],height[right])*(right-left);//重置最大值if(are…

进程间通信基础知识【Linux】——上篇

目录 一&#xff0c;理解进程之间的通信 1. 进程间通信目的 2. 进程间通信的技术背景 3&#xff0c;常见的进程间通信 二&#xff0c;管道 1. 尝试建立一个管道 管道的特点&#xff1a; 管道提供的访问控制&#xff1a; 2. 扩展&#xff1a;进程池 阶段一&#xff1a…

sqli-labs靶场详解(less32-less37)

宽字节注入 原理在下方 目录 less-32 less-33 less-34 less-35 less-36 less-37 less-32 正常页面 ?id1 下面有提示 获取到了Hint: The Query String you input is escaped as : 1\ ?id1 看来是把参数中的非法字符就加上了转义 从而在数据库中只能把单引号当成普通的字…

asla四大开源组件应用示例(alsa-lib、alsa-utils、alsa-tools、alsa-plugins)

文章目录 alsa设备文件/dev/snd//sys/class/sound/proc/asoundalsa-lib示例1alsa-utilsalsa-toolsalsa-plugins参考alsa设备文件 /dev/snd/ alsa设备文件目录位于,/dev/snd,如下所示 root@xboard:~#ls /dev/snd -l total 0 drwxr-xr-x 2 root root 60 Nov 6 2023 …

springboot基础配置及maven运行

目录 1、spring快速开始&#xff1a; 2、通过idea工具打开导入包 3、maven打包 1、springboot快速开始&#xff1a; 环境依赖&#xff1a;jdk17 Spring | Quickstart spring初始化包下载&#xff1a; 点击generate&#xff0c;下载包 2、通过idea工具打开导入包 我之前写了…

【Vulnhub靶机】lampiao--DirtyCow

文章目录 漏洞介绍简介原因类型版本危害 信息收集主机扫描端口扫描 漏洞探测漏洞利用权限提升nc文件传输编译 参考 靶机地址&#xff1a;lampiao 下载地址&#xff1a;Lampio: 1 漏洞介绍 简介 脏牛&#xff08;Dirty Cow&#xff09;是Linux内核的一个提权漏洞&#xff0c;…

Redis 通用命令和数据类型

get和set get和set两个命令是最基本也是最常用的命令&#xff0c;主要用于操作字符串类型的数据。 1.SET 命令: SET 命令用于设置指定 key 的值。如果 key 已经持有其他值&#xff0c;SET 就覆写旧值&#xff0c;无视类型。具体的命令格式如下&#xff1a; SET key value例如…