Jenkins Pipeline 手记(1)—— 什么是CPS编程
引言
最近在工作中使用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 ;
}
这里面的回调函数onSuccess
和onFail
就是所谓的 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编程相关推荐
- Jenkins Pipeline 手记(3)—— 自定义Checkout的陷阱
引言 最近在pipeline中checkout代码时遇到了"不可序列化"(java.io.NotSerializableException)的问题.这个问题只在特定的场景下能重现, ...
- Jenkins Pipeline 语法
目录 声明性pipeline Sections agent pipeline与stage中的不同 参数 常用选项 发布 post stages steps Directives environment ...
- 「Jenkins Pipeline」- expected to call xxx but wound up catching xxx @20210219
问题描述 在 Jenkins Pipeline 中,我们从控制台中看到类似如下提示信息: expected to call org.jfrog.hudson.pipeline.common.types ...
- # 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 ...
- Docker的Jenkins Pipeline工作流
原文地址:http://www.youruncloud.com/blog/127.html 分享主题 一个软件产品的开发周期中,尤其是敏捷开发,持续集成和持续部署是必不可少的环节,而随着产品的丰富,模 ...
- Jenkins Pipeline构建流水线发布
Jenkins Pipeline构建流水线发布 1. Jenkins Pipeline 核心概念 Jenkins Pipeline是一套插件,支持在Jenkins中实现持续集成.交付管道; Pipel ...
- Jenkins Pipeline动态使用Git分支名称的技巧
前言 在上一篇 Jenkins 使用环境变量 中,帮助大家使用一条 Docker 命令就可以快速玩转 Jenkins,同时用最简单的方式解释了 Jenkins 中让人混乱的环境变量,本文还是接着变量说 ...
- Jenkins Pipeline插件十大最佳实践!
Jenkins Pipeline 插件对于 Jenkins 用户来说可以让用户能够改变游戏规则.基于 Groovy 中的领域特定语言(DSL),Pipeline 插件使 Pipelines 可以有脚本 ...
- Jenkins Pipeline 构建复杂的Electron程序
最近一年都在捣腾Electron,从0.X版本,到现在最新Stable的3.X版本.一路上踩的坑很多,Electron的版本号也飞快的.项目设立的初衷是一份代码,即可构建出Windows和Mac,并且 ...
- 【完整示例】采用jenkins pipeline实现自动构建并部署至k8s
前言 在日常开发中,经常会有发布的需求,而且经常会碰到各种环境,比如:开发环境.测试环境.生产环境.虽然可以使用手动构建.上传服务器部署的方式,但在微服务架构下一个项目经常包含多个微服务的部署,如果用 ...
最新文章
- oracle 全局临时变量,如何解析Oracle PL / SQL中的简单XML片段并将其加载到全局临时表中?...
- Glide 源码分析与面试提问
- Silverlight WCF 初尝小结
- 【中级软考】专利权的保护期限可以延长吗?商标使用权呢?
- 技术实战 —— 快速实现语聊房搭建
- 定制安装centos6.4系统 上汽集团线上环境
- Termux配置ssh连接
- SQL server数据异地备份到OSS
- JS:ES6-6 初识Symbol类型
- linux的增删改查、压缩与解压缩
- 在资源管理器中增加二级菜单
- RPC框架dubbo架构原理及使用说明
- tar.bz2 解压命令以及使用指令
- 通过网络数据采集系统快速获得优质销售线索
- 2016word多级列表 一级标题居中后偏左
- Incorrect string value: '\\xE6\\xBF\\x80\\xE5\\x85\\x89...' for column 'rukuName' at row 1 QMYSQL:
- 第二篇数模论文——垂钓问题
- oracle11g64跟32,plsql32 位连接oracle11g64位方法
- MySQL字段名诸如key的报错问题
- 马克思主义哲学(一):物质