Jenkins Pipeline手記(1)-CPSプログラミングとは
引用する
最近、Jenkinsを使用した継続的な統合タスクで、次のような問題が発生しました.
これはJenkinsのCPSプラグインが報告したもので,正規表現に関連する操作の関数を用いた場合,コードの書き方が規範化されず,シーケンス化できないためであることが分かった.ではCPSとは何か、Jenkinsはなぜシーケンス化の操作が必要なのか.
CPSとは
関数式プログラミングでは、CPS (Continuation-Passing Style)は、すべての制御ブロックがcontinuationによって明示的に伝達されるプログラミングスタイルです.簡単に言えば、CPSスタイルでは、関数に戻り文がありません.その呼び出し者がその結果を得るには、結果を取得して実行を続けるためにコールバック関数を明示的に伝達する必要があります.プログラム全体が実行されることを保証するために、このコールバック関数はまたずっとネストされます.ここでのコールバック関数は「continuation」です.しばらく良い翻訳が見つからないので、その原文を残します.
一般的な数学的乗算を計算する関数など、再帰的な例を見てみましょう.
上は伝統的な再帰的実現であり,下はCPSスタイルの実現である.return文はコールバック関数の呼び出しに置き換えられ、この動作に乗算すると、終了条件まで次の再帰でネストされたコールバック関数によって完了することがわかります.各層の再帰では、コールバック関数はパラメータに1つの係数を乗算し、最外層の印刷結果まで上位層のコールバック関数を呼び出すことが理解できます.
CPSの応用シーン
1.Jenkinsでの応用は最初の問題に戻り、JenkinsはなぜCPSプログラミング技術を使うのか.なぜならJenkins Pipelineの実装では,コードの実行をいつでも中断し,状態を保存し,適切なタイミングで実行を再開することが期待されるからである.Jenkins agentのダウンタイムに対応できます.1つの関数が実行された後に返されると,一部の状態が失われ,CPSコードは途中で結果を返さないため,この問題を解決できる.
しかし、あなたもPipelineコードはGroovy言語で、それ自体はCPSスタイルではありません.これは解釈器がコードをCPSスタイルにコンパイルする必要があります.Jenkinsの中でgroovy-cpsというプラグインで完成します.このプラグインの安定性を見て、CPSとGroovy解釈器についてもっと紹介することもできます.
オブジェクトの状態を永続化するにはでは、このオブジェクトは必要に応じてシーケンス化できます.(Serializable).Jenkins Pipelineの状態を保存するには、CPSのコードも直列化することが求められます.いくつかのコードが直列化できない場合、または直列化が安全でない場合は、Pipelineコードに
正規表現に関連する機能を書くときに使用される可能性のある
上記の文の方法を使用する前にマークを宣言するのを忘れた場合、文章の先頭にあるシーケンス不可能な異常が投げ出され、私が遭遇した問題の原因はここにあります.
2.非同期リクエストに適用するもう一つの考えやすいシーンは非同期リクエストであり、JavaScriptコードではリクエストの結果をコールバック関数で処理し、プログラムを停止させないようにすることがよくあります.これはCPSスタイルです
request関数の実装では、Ajaxオブジェクトを使用します.
このうちコールバック関数
参考資料 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を使用した継続的な統合タスクで、次のような問題が発生しました.
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 ;
else
return n * fact(n-1) ;
}
// CPS
function fact(n,ret) {
if (n == 0)
ret(1) ;
else
fact(n-1, function (t0) {
ret(n * t0) }) ;
}
//
fact (5, function (n) {
console.log(n) ; // Output 120
})
上は伝統的な再帰的実現であり,下はCPSスタイルの実現である.return文はコールバック関数の呼び出しに置き換えられ、この動作に乗算すると、終了条件まで次の再帰でネストされたコールバック関数によって完了することがわかります.各層の再帰では、コールバック関数はパラメータに1つの係数を乗算し、最外層の印刷結果まで上位層のコールバック関数を呼び出すことが理解できます.
CPSの応用シーン
1.Jenkinsでの応用は最初の問題に戻り、JenkinsはなぜCPSプログラミング技術を使うのか.なぜならJenkins Pipelineの実装では,コードの実行をいつでも中断し,状態を保存し,適切なタイミングで実行を再開することが期待されるからである.Jenkins agentのダウンタイムに対応できます.1つの関数が実行された後に返されると,一部の状態が失われ,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である.参考資料