![[小ネタ]Go言語でワークフローを実行できる「goflow」を試してみた](https://images.ctfassets.net/ct0aopd36mqt/wp-thumbnail-243509342eb7b967e9e235ee0c61af90/ea6b1c68015d9fbd4fa40d2df85865d3/golang-logo_1200x630.png)
[小ネタ]Go言語でワークフローを実行できる「goflow」を試してみた
Go言語でワークフローを実現する場合、いろいろなやり方があるかと思います。独自に実装するのも良し、ライブラリを使って実現するのも良いかと思います。
今回はできるだけ簡単に実現するため、ライブラリを使うことを検討していたところ「Goflow」というライブラリを見つけました。
この「Goflow」についてと、試した時のプログラムについてを以下に書いていきたいと思います。
Goflowについて
先にも書きましたが、Go言語で実装されたワークフローを実現するためのライブラリです。
A simple but powerful DAG scheduler and dashboard, written in Go.
と先のリンクあるように、シンプルなDAG Schedulerであり、ダッシュボードを含んでいます。
このダッシュボードはジョブの実行状態やタスク構成を見ることができます。
またライブラリのコードもシンプルであり、理解しやすいと感じました。例えば、以降に書くcronの設定方法などもコードを少し読んで理解することができました。
今回書いたプログラムについて
こちらのREADME にあるサンプルとほぼ同じものとなりますが、実現したかった要件に合わせて以下を追加で試してみました。
- ジョブを実行するスケジュールをJSTとし、実行する時間を秒まで指定できるようにした
- ジョブの実行履歴を組み込みのデータベースである bbolt に保持するようにした
ジョブを設定、実行したダッシュボードのキャプチャは以下のようになりました。
プログラムは以下となります。
package main
import (
"errors"
"log"
"net/http"
"github.com/fieldryand/goflow/v2"
bbolt "github.com/philippgille/gokv/bbolt"
)
type PositiveAddition struct{ a, b int }
func (o PositiveAddition) Run() (interface{}, error) {
if o.a < 0 || o.b < 0 {
return 0, errors.New("can't add negative numbers")
}
result := o.a + o.b
return result, nil
}
func myJob() *goflow.Job {
j := &goflow.Job{Name: "my-job", Schedule: "CRON_TZ=Asia/Tokyo 00 22 16 * * *", Active: true}
j.Add(&goflow.Task{
Name: "1秒Sleep",
Operator: goflow.Command{Cmd: "sleep", Args: []string{"1"}},
})
return j
}
func myJob2() *goflow.Job {
j := &goflow.Job{Name: "my-job2", Schedule: "CRON_TZ=Asia/Tokyo 00 58 16 * * *", Active: true}
j.Add(&goflow.Task{
Name: "1秒Sleep",
Operator: goflow.Command{Cmd: "sleep", Args: []string{"1"}},
Retries: 5,
RetryDelay: goflow.ConstantDelay{Period: 1},
})
j.Add(&goflow.Task{
Name: "get-google",
Operator: goflow.Get{Client: &http.Client{}, URL: "https://www.google.com"},
})
j.Add(&goflow.Task{
Name: "add-two-plus-three",
Operator: PositiveAddition{a: 2, b: 3},
})
j.SetDownstream(j.Task("1秒Sleep"), j.Task("get-google"))
j.SetDownstream(j.Task("get-google"), j.Task("add-two-plus-three"))
return j
}
func main() {
store, err := bbolt.NewStore(bbolt.Options{
BucketName: "boltBucket",
Path: "bbolt.db",
})
if err != nil {
log.Fatal(err)
}
defer store.Close()
options := goflow.Options{
Store: store,
UIPath: "ui/",
ShowExamples: false, // not show examples
Streaming: true,
WithSeconds: true,
}
gf := goflow.New(options)
gf.AddJob(myJob)
gf.AddJob(myJob2)
gf.Use(goflow.DefaultLogger())
gf.Run(":8181")
}
キャプチャにある2つのジョブ「my-job」「my-job2」を関数として定義しています。スケジュールはJSTで実行する時間を秒単位で指定しています。
秒単位で指定する場合は「goflow.Options」で「WithSeconds」を「true」とする必要があります。
「main()」の中ではジョブの実行履歴を保持できるよう「bbolt」をStoreとして設定しています。このとき「gokv」を使用しています。
Note: the gokv API is not yet stable. Goflow has been tested against v0.6.0.
「gokv」についてはREADMEに上記のような記載がありますが、今回は最新版(latest)を使用しました。
このプログラムを書き「go run main.go」で実行し、8181ポートをブラウザで開くと、キャプチャとして上げたダッシュボードが表示されてcronで指定した時間にジョブが実行されました。
まとめ
このようにシンプルにワークフローが実現でき、ジョブの状況もダッシュボードで表示されました。Go言語で実装できるということで、ワークフロー処理を実装してシングルバイナリとして配布できるなどのメリットもあるかと思います。
小ネタではありますが、何かの参考になれば幸いです。