Jenkins Pipeline并行调度异常:仅执行最后一个任务问题排查
问题根源:Groovy闭包的延迟绑定
嘿,这个问题我太熟了——这是Groovy闭包延迟绑定导致的经典陷阱,和你用的Jenkins 2.46.3、Pipeline 2.5版本没有直接的“不支持特性”问题,本质是Groovy的变量解析机制在搞鬼。
你在each循环里给branches赋值闭包的时候,闭包并没有立刻“记住”当前循环的jobname值,而是要等到闭包真正执行(也就是parallel启动分支任务的时候)才会去查找jobname变量的当前值。这时候循环早就跑完了,jobname已经被更新成列表里的最后一个元素HW_run4,所以所有分支都会复用这个值,自然就都只跑HW_run4了。
解决方案:捕获当前变量的副本
有两种简单的方法可以解决这个问题,核心都是让每个闭包持有自己独立的jobname副本:
方法1:用局部变量捕获当前值
在循环内部新建一个局部变量,把当前的jobname赋值给它,这样闭包就会绑定这个局部变量,而不是循环里的共享变量:
stage ('test') { def branches = [:] def test_jobs = [:] def jobnames = ["HW_run1", "HW_run2", "HW_run3", "HW_run4"] node ('master') { jobnames.each { jobname -> // 用局部变量保存当前循环的jobname值 def currentJob = jobname branches[currentJob] = { def jj = build job: currentJob test_jobs[currentJob] = jj.getDuration() } } try { parallel branches } catch (e) { println "Unit test stage failed with error " + e } } }
方法2:用闭包的curry方法绑定参数
利用Groovy闭包的curry方法,把当前的jobname值提前绑定到闭包的参数上,确保每个闭包拿到的都是独立的值:
stage ('test') { def branches = [:] def test_jobs = [:] def jobnames = ["HW_run1", "HW_run2", "HW_run3", "HW_run4"] node ('master') { jobnames.each { jobname -> branches[jobname] = { String currentJob -> def jj = build job: currentJob test_jobs[currentJob] = jj.getDuration() }.curry(jobname) // 把当前jobname绑定到闭包参数 } try { parallel branches } catch (e) { println "Unit test stage failed with error " + e } } }
补充说明
你提到“不使用列表则一切正常”,是因为直接写每个分支的时候,每个闭包对应的变量都是独立定义的,不存在循环里共享变量被覆盖的问题,自然不会触发延迟绑定的陷阱。
内容的提问来源于stack exchange,提问作者Zoe Slattery
相关产品推荐
相关产品推荐

