7.5 KiB
7.5 KiB
根据Jenkins官方文档,Jenkins的实际层级组织结构如下:
Jenkins的实际层级结构
Jenkins在使用Organization Folder(组织文件夹)功能时,采用的层级结构为:
Organization Folder(组织)
└── Repository/Project(仓库/项目)
└── Branch(分支)
└── Build(构建)
官方文档中明确展示的层级示例:
+--- GitHub Organization
+--- Project 1
+--- master
+--- feature-branch-a
+--- feature-branch-b
+--- Project 2
+--- master
+--- pull-request-1
关键区别
"Projects → Repository"实际上在Jenkins中是同一层级的概念。 Jenkins使用以下术语:
- Organization Folder:对应GitHub Organization、Bitbucket Team、GitLab Organization等组织级别
- Multibranch Pipeline Project:对应单个代码仓库,会自动为该仓库的所有包含Jenkinsfile的分支创建构建任务
- Branch:具体的分支或Pull Request
- Build:针对某个分支的具体构建执行
实现逻辑
Jenkins通过以下机制实现这种层级:
- Multibranch Pipeline自动扫描单个仓库,为每个包含Jenkinsfile的分支创建Pipeline任务
- Organization Folder自动扫描整个组织,为每个符合条件的仓库创建Multibranch Pipeline项目
- 当分支或仓库被创建/删除时,可以通过webhook或定期扫描自动更新Jenkins中的任务结构
因此,准确的表述应该是:Organization → Repository(Project) → Branch → Build,其中Repository和Project是等价的概念。
根据Jenkins官方文档和API最佳实践,您可以通过以下方式获取GitLab Organization Folder的所有分支和构建信息:
推荐方案:使用Tree参数的单次API调用
最高效的方式是使用tree参数,通过一次API调用获取所有数据:
https://<JENKINS_HOST>/job/<ORGANIZATION_FOLDER_NAME>/api/json?tree=jobs[name,url,jobs[name,url,builds[number,result,timestamp,duration,url]]]
参数说明
- 第一层
jobs:Organization Folder下的所有Repository(Multibranch Pipeline项目) - 第二层
jobs:每个Repository下的所有Branch(分支作为子job存在) builds:每个Branch的构建历史
限制返回的构建数量
如果构建历史过多,可以限制返回数量(例如最近50次):
https://<JENKINS_HOST>/job/<ORGANIZATION_FOLDER_NAME>/api/json?tree=jobs[name,url,jobs[name,url,builds[number,result,timestamp,duration,url]{0,50}]]
{0,50}表示只返回最近50次构建。
API调用结构层次
基于Organization Folder的结构,API调用路径遵循以下层级:
- Organization Folder →
/job/<FOLDER_NAME> - Repository (Multibranch Pipeline) →
/job/<FOLDER_NAME>/job/<REPO_NAME> - Branch →
/job/<FOLDER_NAME>/job/<REPO_NAME>/job/<BRANCH_NAME> - Build →
/job/<FOLDER_NAME>/job/<REPO_NAME>/job/<BRANCH_NAME>/<BUILD_NUMBER>
完整示例
假设您的Organization Folder名为CMII-UAV-Cloud-Backend-GITLAB:
# 获取所有仓库、分支和最近50次构建
curl -u username:api_token \
"https://<JENKINS_HOST>/job/CMII-UAV-Cloud-Backend-GITLAB/api/json?tree=jobs[name,url,jobs[name,url,builds[number,result,timestamp,duration,url]{0,50}]]"
性能优化建议
- 避免使用
depth参数:depth会返回大量不必要的数据,建议使用tree参数精确指定所需字段 - 限制构建数量:使用
{0,N}语法限制每个分支返回的构建数量 - 按需获取字段:只请求必要的字段,减少数据传输量
分析您的API调用性能问题,主要原因是Jenkins需要遍历整个Organization Folder的所有仓库、分支和构建数据,即使您只请求了前3条记录。 以下是性能瓶颈和优化方案:
性能瓶颈分析
主要问题
- 一次性加载所有数据:Jenkins API的
tree参数在返回前必须先遍历整个数据树结构,即使使用{0,3}限制构建数量 - Organization Folder扫描开销:每个多分支项目的分支发现和元数据加载都会产生大量I/O操作
- 深层嵌套查询:三层嵌套(Organization → Repository → Branch → Builds)导致指数级的数据查询
为什么{0,3}不起作用
{0,3}只限制每个分支返回的构建数量,但Jenkins仍需要:
- 遍历所有Repository
- 扫描每个Repository的所有分支
- 为每个分支加载元数据
高效解决方案
方案1:分层逐级获取(推荐)
不要一次性获取所有层级,而是分步骤获取:
步骤1:获取所有Repository
curl -g -u username:token \
"http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB/api/json?tree=jobs[name,url]"
返回速度快,只获取Repository列表。
步骤2:并行获取每个Repository的分支
# 对每个Repository
curl -g -u username:token \
"http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB/job/<REPO_NAME>/api/json?tree=jobs[name,url,lastBuild[number,result,timestamp,duration]]"
使用lastBuild只获取最新构建,而非所有构建历史。
步骤3:按需获取具体分支的构建历史
curl -g -u username:token \
"http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB/job/<REPO_NAME>/job/<BRANCH_NAME>/api/json?tree=builds[number,result,timestamp,duration]{0,10}"
方案2:只获取关键信息
如果只需要统计数据,使用最小化字段:
curl -g -u username:token \
"http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB/api/json?tree=jobs[name,jobs[name,lastBuild[number,result,timestamp]]]"
关键优化点:
- 移除
url字段(可自行构造) - 只用
lastBuild替代builds[] - 减少返回字段数量
方案3:使用Python脚本异步并发获取
import asyncio
import aiohttp
from aiohttp import BasicAuth
async def fetch_repos(session, base_url, auth):
url = f"{base_url}/api/json?tree=jobs[name]"
async with session.get(url, auth=auth) as resp:
data = await resp.json()
return [job['name'] for job in data.get('jobs', [])]
async def fetch_repo_branches(session, base_url, repo_name, auth):
url = f"{base_url}/job/{repo_name}/api/json?tree=jobs[name,lastBuild[number,result,timestamp]]"
async with session.get(url, auth=auth) as resp:
data = await resp.json()
return {
'repo': repo_name,
'branches': data.get('jobs', [])
}
async def main():
base_url = "http://192.168.34.41:27081/job/CMII-UAV-Cloud-Backend-GITLAB"
auth = BasicAuth('zeaslity', '114f14f0c48fd9dfb4d3092426b0f72913')
async with aiohttp.ClientSession() as session:
# 1. 获取所有仓库
repos = await fetch_repos(session, base_url, auth)
print(f"找到 {len(repos)} 个仓库")
# 2. 并发获取所有仓库的分支信息
tasks = [fetch_repo_branches(session, base_url, repo, auth) for repo in repos]
results = await asyncio.gather(*tasks)
# 3. 输出结果
for result in results:
print(f"\n仓库: {result['repo']}")
for branch in result['branches']:
build = branch.get('lastBuild', {})
if build != None:
print(f" 分支: {branch['name']}, 最新构建: #{build.get('number', 'N/A')}")
asyncio.run(main())
这个脚本将3分钟的串行请求压缩到数秒内。