https://www.bilibili.com/video/BV1RqNRz5Eo6
Jenkins是一款常見的構建管理工具,配置好后操作也很簡單,只需去控制臺找到對應的項目,再輸入分支名即可
如果每次只發個位數的項目到也還好,一個個進去點嘛。但如果一次要發幾十個項目呢?這就很費時費力了。好在Jenkins提供了rest接口,可以通過接口來進行批量構建
一、功能描述
目的:使用 Jenkins-rest,多線程批量構建項目
構建項目,需要2個參數
- jobName, 對應jenkins上的名稱
- 構建參數格式
Map<String, List<String>>
,主要是分支名,如果還有其它的也可以加進去
構建的代碼
IntegerResponse response = jobsApi.buildWithParameters(null, jobName, buildProperties);
構建完之后肯定是要獲取到構建結果的
- 構建后返回一個 queueId
- 通過 queueId,獲取 buildNumber
- 通過 buildNumber 獲取BuildInfo,BuildInfo里有我們想要的結果參數
二、代碼實現
因為涉及到公司的代碼,這里只給出核心的流程,業務參數就不給出了
private void build(JenkinsBuildRequest request) {logger.info("jenkins構建開始:JenkinsBuildRequest:{}", request);JenkinsBuildInfo jenkinsBuildInfo = initJenkinsBuildInfo(request);try (JenkinsClient client = JenkinsClient.builder().endPoint(jenkinsUrl).credentials(jenkinsCredentials).build()) {JobsApi jobsApi = client.api().jobsApi();QueueApi queueApi = client.api().queueApi();Map<String, List<String>> buildProperties = buildProperties(request);String jobName = request.generateJobName();IntegerResponse response = jobsApi.buildWithParameters(null, jobName, buildProperties);if (!response.errors().isEmpty()) {logger.error("jenkins構建失敗: planId:{}, jobName:{}, responseError:{}", request.getPlanId(), jobName, response.errors());jenkinsBuildInfo.setStatus(JenkinsStatusEnum.FAILURE.getValue());updateBuildInfo(request, jenkinsBuildInfo);return;}int queueId = response.value();jenkinsBuildInfo.setQueueId(queueId);// 有queueId 沒有number的時候就是構建中jenkinsBuildInfo.setStatus(JenkinsStatusEnum.BUILDING.getValue());updateBuildInfo(request, jenkinsBuildInfo);JenkinsStatusEnum status = pollBuildStatusAndSetBuildInfo(queueId, jobName, jobsApi, queueApi, jenkinsBuildInfo);jenkinsBuildInfo.setStatus(status.getValue());updateBuildInfo(request, jenkinsBuildInfo);logger.info("jenkins構建成功: planId:{}, jobName:{}", request.getPlanId(), jobName);} catch (Exception e) {logger.error("jenkins構建失敗: JenkinsBuildRequest:{}", request, e);jenkinsBuildInfo.setStatus(JenkinsStatusEnum.FAILURE.getValue());updateBuildInfo(request, jenkinsBuildInfo);}
}/*** 輪詢獲取Jenkins的構建狀態*/
private JenkinsStatusEnum pollBuildStatusAndSetBuildInfo(int queueId, String jobName, JobsApi jobsApi, QueueApi queueApi, JenkinsBuildInfo jenkinsBuildInfo) throws InterruptedException {Integer buildNumber = null;// 每次輪詢間隔3秒,最多輪詢200次,共10分鐘for (int attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {// 獲取構建編號:如果已經構建結束了調用 queueId 會報錯找不到資源,所以獲取到buildNumber之后,就不用再去獲取 QueueItemif (buildNumber == null) {QueueItem queueItem = queueApi.queueItem(queueId);if (queueItem.executable() != null) {buildNumber = queueItem.executable().number();}}// 如果構建編號已確定,獲取構建信息并檢查構建狀態if (buildNumber != null) {BuildInfo buildInfo = jobsApi.buildInfo("", jobName, buildNumber);// 如果構建已完成,設置構建信息并返回狀態if (!buildInfo.building()) {jenkinsBuildInfo.setUrl(buildInfo.url());jenkinsBuildInfo.setNumber(buildNumber);return "SUCCESS".equals(buildInfo.result()) ? JenkinsStatusEnum.SUCCESS : JenkinsStatusEnum.FAILURE;}}// 每次輪詢間隔3秒Thread.sleep(POLL_INTERVAL_MS);}logger.error("jenkins構建失敗:輪詢超時: jobName:{}, jenkinsBuildInfo:{}", jobName, jenkinsBuildInfo);return JenkinsStatusEnum.FAILURE;
}
可根據jenkins上打包的節點,多線程去調用 build 方法。 我們是5個節點,最多60個項目,我的線程配置如下
@Bean(name = "jenkinsBuildExecutor")
public ThreadPoolExecutor jenkinsBuildExecutor() {return new ThreadPoolExecutor(4,4,60L, TimeUnit.SECONDS,new ArrayBlockingQueue<>(120));
}
jenkinsUrl
和 jenkinsCredentials
就對應訪問前綴和帳號密碼,格式如下
- https://jenkins.xxxxx.com/
- admin:123456
buildProperties 構建參數,看實際需要什么參數,我這里有一個必填的branch和一個選填的 version
private Map<String, List<String>> buildProperties(JenkinsBuildRequest request) {Map<String, List<String>> params = Maps.newHashMapWithExpectedSize(4);params.put("branch", Lists.newArrayList(request.getBranch()));if (StringUtils.isNotBlank(request.getVersion())) {params.put("version", Lists.newArrayList(request.getVersion()));}return params;
}
JenkinsBuildInfo 是需要存儲這次構建的參數,比如 分支、項目名、操作人、操作時間、構建狀態、結果鏈接什么的
pollBuildStatusAndSetBuildInfo 方法是輪訓獲取結果的,queueApi.queueItem(queueId)
可能會null異常,所以當拿到 buildNumber 之后就不要再去調用它了
updateBuildInfo 方法是去更新本地的數據庫,具體實現看實際業務
三、相關文檔
https://cdancy.github.io/jenkins-rest/docs/javadoc/
核心 API 模塊
API 類別 | API 名稱 | 主要方法 | 功能描述 |
---|---|---|---|
JobsApi | 任務管理 API | build() | 觸發無參數構建任務 |
buildWithParameters() | 觸發帶參數的構建任務 | ||
buildInfo() | 獲取構建詳細信息 | ||
jobInfo() | 獲取任務詳細信息 | ||
create() | 創建新的 Jenkins 任務 | ||
delete() | 刪除 Jenkins 任務 | ||
config() | 獲取/設置任務配置 | ||
disable() | 禁用任務 | ||
enable() | 啟用任務 | ||
lastBuildNumber() | 獲取最后構建編號 | ||
lastBuildTimestamp() | 獲取最后構建時間戳 | ||
progressiveText() | 獲取構建進度日志 | ||
QueueApi | 構建隊列 API | queueItem() | 獲取隊列項目信息 |
cancel() | 取消隊列中的構建 | ||
list() | 列出隊列中的所有項目 | ||
SystemApi | 系統信息 API | systemInfo() | 獲取 Jenkins 系統信息 |
PluginManagerApi | 插件管理 API | installNecessaryPlugins() | 安裝必要的插件 |
list() | 列出當前安裝的插件 | ||
StatisticsApi | 統計信息 API | overallLoad() | 獲取整體負載統計 |