引言

最近在工作中使用Jenkins进行持续集成任务,遇到这样一个问题:

java.io.NotSerializableException: java.util.regex.Matcher

经过调查发现这是Jenkins的CPS插件报出的,原因是在使用正则表达式相关操作的函数时,代码写法不规范,导致不能序列化。那么什么是CPS呢, Jenkins为什么又需要做序列化的操作呢?

什么是CPS

在函数式编程中,CPS (Continuation-Passing Style)是一种编程风格:所有的控制块都通过 continuation 来显式传递。简单来说,在CPS风格中,函数不能有返回语句,它的调用者要想获得它的结果,需要显式传递一个回调函数来获取结果并继续执行。而为了保证整个程序执行下去,这个回调函数还会一直嵌套下去。这里的回调函数就是一个“continuation”。暂时找不到好的翻译,就保留它的原文。

下面来看一个递归的例子,比如常见的计算数学阶乘的函数:

// 原来代码
function fact(n) {if (n == 0)return 1 ;elsereturn n * fact(n-1) ;
}// CPS风格代码
function fact(n,ret) {if (n == 0)ret(1) ;elsefact(n-1, function (t0) {ret(n * t0) }) ;
}// 调用入口
fact (5, function (n) {console.log(n) ; // Output 120
})

上面是传统的递归实现,下面是CPS风格的实现。可以看到return语句被替换成一个回调函数的调用,而相乘这个动作会在接下来的递归里通过嵌套的回调函数完成,直到终止条件。你可以理解为,在每一层的递归中,回调函数都会给参数多乘上一个系数,并调用上一层的回调函数,直到最外层打印结果。

CPS的应用场景

1. 在Jenkins中的应用
回到最初的问题,Jenkins为什么会使用CPS编程技术呢?原因是在Jenkins Pipeline的实现中,期望在任何时候都可以中断代码的执行,保存状态,并在适当时候恢复执行。这可以应对Jenkins agent宕机的场景。如果一个函数执行过后就返回了,那么就会丢失一部分状态,CPS代码由于在中间不返回结果,因此可以解决这个问题。

然而,你也的Pipeline代码是Groovy语言的,其本身并不是CPS风格的,这就需要一个解释器将代码编译成CPS风格,Jenkins里面通过 groovy-cps 这个插件来完成。你也可以查看这个插件的稳定获得更多的关于CPS和Groovy解释器的介绍。

我们知道,如果想要持久化一个对象的状态,那么这个对象需要时可序列化的(Serializable)。Jenkins如果想要保存Pipeline的状态,就会要求CPS的代码也是可序列化的。如果有一些代码是不可序列化,或者序列化不安全的,我们可以在Pipeline代码中加上@NonCPS标记。并且,声明了该标记的函数也只能调用其他有该标记的函数。Jenkins会对这类函数进行常规编译,不生成可序列化的CPS代码。

在写跟正则表达式相关的功能时,可能会用到的~操作符就是序列化不安全,因此要声明标记@NonCPS

def t = (text =~ TEST_REGEX)

如果忘记在使用上面语句的方法前声明该标记,就会抛出文章开头的不可序列化的异常,我遇到的问题的原因就在此。

2. 在异步请求中应用
另外一个我们容易想到的场景就是异步请求,我们在JavaScript代码里经常用回调函数来处理请求的结果,并保持程序不被挂起。这其实就一种CPS风格:

request("./index", function (text) {document.getElementById("test").innerHTML = text ;
}) ;

request函数的实现使用Ajax对象:

function request (url, onSuccess, onFail) {var req ;function process() {if (req.readyState == 4) {if (req.status == 200) {if (onSuccess)onSuccess(req.responseText, url, req) ;} else {if (onFail)onFail(url, req) ;}}}if (window.XMLHttpRequest)req = new XMLHttpRequest();req.onreadystatechange = process;req.open("GET", url, async);req.send(null);return req ;
}

这里面的回调函数onSuccessonFail就是所谓的 continuation

参考资料

  • http://matt.might.net/articles/by-example-continuation-passing-style/
  • https://github.com/jenkinsci/workflow-cps-plugin
  • https://github.com/cloudbees/groovy-cps/
  • https://en.wikipedia.org/wiki/Continuation-passing_style

Jenkins Pipeline 手记(1)—— 什么是CPS编程相关推荐

  1. Jenkins Pipeline 手记(3)—— 自定义Checkout的陷阱

    引言 最近在pipeline中checkout代码时遇到了"不可序列化"(java.io.NotSerializableException)的问题.这个问题只在特定的场景下能重现, ...

  2. Jenkins Pipeline 语法

    目录 声明性pipeline Sections agent pipeline与stage中的不同 参数 常用选项 发布 post stages steps Directives environment ...

  3. 「Jenkins Pipeline」- expected to call xxx but wound up catching xxx @20210219

    问题描述 在 Jenkins Pipeline 中,我们从控制台中看到类似如下提示信息: expected to call org.jfrog.hudson.pipeline.common.types ...

  4. # 2021-01-03 #「Jenkins Pipeline」- expected to call xxx but wound up catching xxx

    问题描述 在 Jenkins Pipeline 中,我们从控制台中看到类似如下提示信息: expected to call org.jfrog.hudson.pipeline.common.types ...

  5. Docker的Jenkins Pipeline工作流

    原文地址:http://www.youruncloud.com/blog/127.html 分享主题 一个软件产品的开发周期中,尤其是敏捷开发,持续集成和持续部署是必不可少的环节,而随着产品的丰富,模 ...

  6. Jenkins Pipeline构建流水线发布

    Jenkins Pipeline构建流水线发布 1. Jenkins Pipeline 核心概念 Jenkins Pipeline是一套插件,支持在Jenkins中实现持续集成.交付管道; Pipel ...

  7. Jenkins Pipeline动态使用Git分支名称的技巧

    前言 在上一篇 Jenkins 使用环境变量 中,帮助大家使用一条 Docker 命令就可以快速玩转 Jenkins,同时用最简单的方式解释了 Jenkins 中让人混乱的环境变量,本文还是接着变量说 ...

  8. Jenkins Pipeline插件十大最佳实践!

    Jenkins Pipeline 插件对于 Jenkins 用户来说可以让用户能够改变游戏规则.基于 Groovy 中的领域特定语言(DSL),Pipeline 插件使 Pipelines 可以有脚本 ...

  9. Jenkins Pipeline 构建复杂的Electron程序

    最近一年都在捣腾Electron,从0.X版本,到现在最新Stable的3.X版本.一路上踩的坑很多,Electron的版本号也飞快的.项目设立的初衷是一份代码,即可构建出Windows和Mac,并且 ...

  10. 【完整示例】采用jenkins pipeline实现自动构建并部署至k8s

    前言 在日常开发中,经常会有发布的需求,而且经常会碰到各种环境,比如:开发环境.测试环境.生产环境.虽然可以使用手动构建.上传服务器部署的方式,但在微服务架构下一个项目经常包含多个微服务的部署,如果用 ...

最新文章

  1. oracle 全局临时变量,如何解析Oracle PL / SQL中的简单XML片段并将其加载到全局临时表中?...
  2. Glide 源码分析与面试提问
  3. Silverlight WCF 初尝小结
  4. 【中级软考】专利权的保护期限可以延长吗?商标使用权呢?
  5. 技术实战 —— 快速实现语聊房搭建
  6. 定制安装centos6.4系统 上汽集团线上环境
  7. Termux配置ssh连接
  8. SQL server数据异地备份到OSS
  9. JS:ES6-6 初识Symbol类型
  10. linux的增删改查、压缩与解压缩
  11. 在资源管理器中增加二级菜单
  12. RPC框架dubbo架构原理及使用说明
  13. tar.bz2 解压命令以及使用指令
  14. 通过网络数据采集系统快速获得优质销售线索
  15. 2016word多级列表 一级标题居中后偏左
  16. Incorrect string value: '\\xE6\\xBF\\x80\\xE5\\x85\\x89...' for column 'rukuName' at row 1 QMYSQL:
  17. 第二篇数模论文——垂钓问题
  18. oracle11g64跟32,plsql32 位连接oracle11g64位方法
  19. MySQL字段名诸如key的报错问题
  20. 马克思主义哲学(一):物质

热门文章

  1. ele饿了么表单验证的校验提示突破v-if的限制
  2. android barchart设置标题,在MPAndroidChart库的BarChart栏中显示轴值
  3. Android模拟器Root
  4. 一代、二代、三代测序技术原理与比较
  5. 眼睛干涩胀痛:得了干眼症该怎么办?
  6. 【论文笔记】ego_planner
  7. Python基础 模块化编程(模块的导入)
  8. 2022年数学建模C题优秀论文
  9. Android动态设置view的大小及其位置
  10. 本科计算机er发篇论文,其实没有那么难