Jenkins@NonCPS函数抛出NotSerializable异常,执行失败排查
Jenkins流水线@NonCPS函数触发Matcher序列化异常的原因与解决方法
问题原因
异常核心是**java.util.regex.Matcher对象泄漏到了流水线全局上下文**:
- 在
findRuntimeKitVersion函数的闭包中,result变量未用def声明,导致它被绑定到流水线的全局脚本作用域,而非函数/闭包的局部作用域。 - Jenkins流水线会在步骤间隙序列化整个上下文状态以支持断点续跑,而
Matcher类本身不实现Serializable接口,因此序列化时抛出NotSerializableException。 - 你观察到“第一个函数执行完才触发异常”,是因为Jenkins在函数执行完成后、进入下一个流水线步骤前,才会执行上下文序列化快照,此时才检测到不可序列化的
Matcher对象。
解决方法
修复findRuntimeKitVersion函数
将result变量声明为闭包内的局部变量,避免泄漏到全局上下文,同时优化匹配逻辑:
@NonCPS def findRuntimeKitVersion (xml) { def xmlparsed = new XmlSlurper(false,true).parseText(xml) echo xmlparsed.attributes()['count'] + " files found" def versionFound // 用find替代findAll+each,仅获取第一个匹配的文件节点 def targetFile = xmlparsed.file.find { def fileNode -> // 声明局部Matcher变量,限定在闭包作用域内 def matcher = fileNode.@name.text() =~ /.*-(.*)-RuntimeKit.*/ matcher.matches() } if (targetFile) { def matcher = targetFile.@name.text() =~ /.*-(.*)-RuntimeKit.*/ versionFound = matcher[0][1] echo "Found version " + versionFound } if (!versionFound) { error ("Product version was not found") } else { return versionFound } }
优化findRuntimeKitPath函数
虽然该函数没有泄漏变量,但可以优化写法避免潜在问题:
@NonCPS def findRuntimeKitPath (xml) { def xmlparsed = new XmlSlurper(false,true).parseText(xml) echo xmlparsed.attributes()['count'] + " files found" def href def targetFile = xmlparsed.file.find { fileNode -> fileNode.@name.text() =~ /.*-(.*)-RuntimeKit.*/ } if (targetFile) { href = targetFile.content.@href.text() } if (!href) { error ("Product path was not found") } else { return href } }
额外建议
- @NonCPS函数应尽量只包含纯Groovy逻辑,Jenkins流水线步骤(如
echo)建议移到函数外部执行,避免非预期的上下文交互。 - 当仅需匹配第一个符合条件的元素时,优先使用
find替代findAll+each,减少不必要的遍历和变量操作。
内容的提问来源于stack exchange,提问作者kab00m
相关产品推荐
相关产品推荐

