Jenkins脚本式Pipeline读取txt文件循环报NotSerializableException异常
问题成因
抛出java.io.NotSerializableException: java.util.ArrayList$Itr异常的核心原因如下:
- Jenkins脚本式Pipeline基于CPS引擎执行,运行过程中会定期将流水线上下文序列化生成持久化快照,用于故障恢复、跨节点调度等场景,所有留存于Pipeline上下文的对象必须实现
Serializable接口,否则就会触发序列化异常。 - 原有实现存在两处逻辑错误:
readFile步骤读取文件返回的是带换行符的完整字符串,并非按行拆分的列表结构;且env全局变量仅支持存储字符串类型,无法直接保存结构化列表对象,直接将读取到的字符串存入env.Mydata本身就不符合变量使用规则。- 直接对未做格式转换的变量调用
each方法遍历时,Groovy隐式生成的ArrayList内部迭代器ArrayList$Itr属于私有内部类,未实现序列化接口,被CPS引擎纳入上下文快照捕获范围时,就会直接抛出异常。
修复方案
按以下逻辑调整代码即可,提供两种可稳定运行的实现方式:
- 注解封装方式(推荐,兼容所有Groovy原生语法)
用@NonCPS注解修饰数据处理逻辑,被该注解标记的方法不会被CPS引擎做序列化拦截,内部可以正常使用Groovy原生的迭代语法:// 读取文件后按换行拆分,去除每行首尾空白、过滤空行,生成可序列化的ArrayList def mydata = readFile(file: 'values.txt').split('\n').collect { it.trim() }.findAll { !it.isEmpty() } // 标注@NonCPS,跳过该方法的CPS序列化检查 @NonCPS def processItemList(List itemList) { itemList.each { currentVal -> // 此处填写原有遍历业务逻辑 echo "当前处理值:${currentVal}" } } // 调用方法执行遍历 processItemList(mydata) - CPS兼容循环方式(无需额外注解)
不使用each闭包迭代语法,改用基于索引的for循环实现遍历,不会生成非序列化的迭代器对象,天然适配CPS引擎的序列化要求:def mydata = readFile(file: 'values.txt').split('\n').collect { it.trim() }.findAll { !it.isEmpty() } // 基于索引的for循环,无额外非序列化对象生成 for (int i = 0; i < mydata.size(); i++) { def currentVal = mydata[i] // 此处填写原有遍历业务逻辑 echo "当前处理值:${currentVal}" }
注意事项:代码中所有引号、括号必须使用英文半角符号,不要使用中文全角符号,否则会触发语法解析错误;不要将列表、Map这类结构化对象存入
env变量,跨阶段传递结构化数据直接使用def定义的局部变量即可。
内容的提问来源于stack exchange,提问作者user15824359
相关产品推荐
相关产品推荐

