Jenkins Pipeline 中使用 JsonSlurper 报错:cannot find current thread
Jenkins Pipeline 中使用 JsonSlurper 报错:cannot find current thread
- 🌟 背景
- ⚠ 问题重现
- 🧠 原因解析:CPS 与非 CPS 安全方法冲突
- ✅ 解决方案一:使用 @NonCPS 注解(经典方案)
- ✅ 解决方案二:使用 `readJSON` 步骤(推荐)
- ✅ 解决方案三:完全在 `script {}` 中处理(适合小范围)
- 🔁 最佳实践推荐
- ❌ 避免的错误写法
- 📌 总结
🌟 背景
在 Jenkins 声明式 Pipeline 中,有时我们需要解析一段 JSON 字符串,例如部署路径、构建参数等。在 Groovy 中,最常见的方式是使用 JsonSlurper
:
def jsonData = new groovy.json.JsonSlurper().parseText(myJsonText)
然而,在 Jenkins 中你很可能会遇到如下报错:
java.io.IOException: cannot find current thread
这类报错常常让人摸不着头脑。为什么在 Groovy 中正常工作的代码,在 Jenkins Pipeline 中却报错?
⚠ 问题重现
pipeline {agent anystages {stage('Parse JSON') {steps {script {def jsonText = '[{"src":"/a","dest":"/b"}]'def deployList = new groovy.json.JsonSlurper().parseText(jsonText) // 报错行}}}}
}
运行报错:
Cannot contact <node>: java.io.IOException: cannot find current thread
🧠 原因解析:CPS 与非 CPS 安全方法冲突
Jenkins Pipeline 基于 Groovy CPS(Continuation Passing Style)转换机制 实现“流水线可恢复性”。这意味着:
- Pipeline 中的脚本会被 Jenkins 转换成 CPS 代码
- CPS 会将执行状态保存到磁盘,以支持“中断恢复”、“断点续跑”
- 然而,一些方法(如
JsonSlurper.parseText()
)不是 CPS 安全的,即不能被 Jenkins 正确序列化和恢复
✴ 为什么会报错?
JsonSlurper
会在底层调用 Thread.currentThread()
、或使用 Java 原生 IO API,这在 CPS 上下文中是不被支持的操作。因此 Jenkins 抛出:
java.io.IOException: cannot find current thread
本质上,是 Jenkins 的 CPS 执行引擎无法“保存你执行的上下文状态”。
✅ 解决方案一:使用 @NonCPS 注解(经典方案)
将解析方法单独封装,并添加 @NonCPS
注解:
@NonCPS
def parseDeployPath(String jsonText) {try {if (jsonText == null || jsonText.trim() == "") {// 输入为空,返回空列表return []}def rawList = new groovy.json.JsonSlurper().parseText(jsonText)def simpleList = rawList.collect { item -> [src: item.src.toString(), dest: item.dest.toString()]}return simpleList} catch (Exception e) {println "❌ JSON 解析 deployPath 失败:${e.message}"return null}
}pipeline {agent anystages {stage('Parse') {steps {script {def json = '[{"src":"/a","dest":"/b"}]'def deployList = parseDeployPath(json)echo "Deploy List: ${deployList}"}}}}
}
为什么可行?
使用 @NonCPS
修饰的方法,不会被 Jenkins 的 CPS 引擎转换,因此可以使用原生 Groovy 方法,但代价是:
- 无法使用 DSL(如
sh
,echo
等) - 方法内不可访问 Pipeline 变量(如
env
,params
)
✅ 解决方案二:使用 readJSON
步骤(推荐)
安装插件:Pipeline Utility Steps
pipeline {agent anystages {stage('Parse JSON safely') {steps {script {writeFile file: 'deploy.json', text: '[{"src":"/a","dest":"/b"}]'def deployList = readJSON file: 'deploy.json'deployList.each {echo "src: ${it.src}, dest: ${it.dest}"}}}}}
}
优势:
readJSON
是 Jenkins 官方提供的 DSL 级方法- 完全支持 流水线序列化和恢复
- 不需要使用
@NonCPS
✅ 解决方案三:完全在 script {}
中处理(适合小范围)
虽然 JsonSlurper
本身不是 CPS 安全的,但在某些场景下,如果你在 script {}
中直接使用它,而没有调用嵌套函数,也能正常工作。
script {def json = '[{"src":"/a","dest":"/b"}]'def list = new groovy.json.JsonSlurper().parseText(json)list.each {echo "src: ${it.src}, dest: ${it.dest}"}
}
⚠ 注意:这种方式不一定在所有 Jenkins 环境中都安全,视具体版本而定。
🔁 最佳实践推荐
场景 | 推荐方式 |
---|---|
生产级流水线中解析 JSON | ✅ readJSON |
工具方法、辅助转换 | ✅ @NonCPS |
快速测试、数据调试 | ✅ script { JsonSlurper } |
❌ 避免的错误写法
不要这样写:
def deployList = new groovy.json.JsonSlurper().parseText(params.jsonData)
或嵌套在函数中调用:
def parseIt() {return new JsonSlurper().parseText('...')
}
✅ 改为 @NonCPS 修饰 或使用 readJSON
📌 总结
Jenkins Pipeline 中,Groovy 方法并非都能直接使用。受限于 Jenkins 的 CPS 系统,很多涉及 IO、线程、状态不可序列化的操作会报错。面对 JsonSlurper
报错,推荐采用:
- 首选:使用 Jenkins DSL 提供的
readJSON
+writeFile
- 通用:将 JSON 操作封装为
@NonCPS
方法 - 调试:仅在
script {}
中临时使用JsonSlurper