🍣

Cloud Tasksを使ってみた

2021/03/01に公開

はじめに

Cloud Tasksで下記を試したときの備忘録です。

  1. cloud taskを動かす
  2. リトライ処理の確認

Cloud Tasksとは

完全マネージド型のメッセージキューサービスです。
大量のタスクの分散実行や配信管理、リトライ処理を行うことが可能です。(違いは結構ありますが、)AWSのSQSと似たようなサービスです。

以下はサービスのイメージをまとめてみました。

まず最初に用語の確認をしておきます。

用語整理

  • タスク
    1処理を実行するためのメッセージ
  • キュー
    複数のタスクを管理するもの
  • タスクハンドラ(ワーカー)
    タスクを処理するサービス

下記は全体の流れです。

  1. タスク作成のアプリケーションがタスクを作製してキューに送る
  2. キューは設定されたタスクを管理して、キューの設定に従いタスクハンドラーに配信(dispatch)する
  3. タスクハンドラーは処理を実行する

実行するハンドラを指定するのは、タスク作製時です。
つまり同じキューに入れても別々のハンドラに送信することが可能です。

事前準備

最初にタスクを実行するハンドラー作製します。今回はCloud functionを使用します。
index.jsにログを出力するだけの関数を作成します。
Taskを作成する際、bodyに値を追加することができるので内容が正しく渡せているかをログで確認します。

適当なディレクトリにindex.jsを作製します。

index.js
exports.taskHandler1 = (req, res) => {
  console.info("body:", req.body)
  res.status(200).send("taskHandler1 called!!");
}

作成できたら、deployします。

$ gcloud functions deploy taskHandler1 --runtime=nodejs12 --trigger-http --region asia-northeast1

// 正しくデプロイできたかを確認
$ curl https://asia-northeast1-${PROJECT}.cloudfunctions.net/taskHandler1
taskHandler1 called!!

Queueの作成

次はキューの作成します。

gcloud tasks queues create testQueue1

作製が完了したら次のコマンドで内容を確認できます。
gcloud tasks queues describe testQueue1

name: projects/${PROJECT}/locations/asia-northeast1/queues/testQueue1
rateLimits:
  maxBurstSize: 100
  maxConcurrentDispatches: 1000
  maxDispatchesPerSecond: 500.0
retryConfig:
  maxAttempts: 100
  maxBackoff: 3600s
  maxDoublings: 16
  minBackoff: 0.100s
state: RUNNING

キューの構成についてはこちらの公式ドキュメントから確認できます。
https://cloud.google.com/tasks/docs/configuring-queues?hl=ja

Taskの作成

動作確認

実際にタスクを作成します。今回は、gcloudを使います。
言語別のサンプルコードはこちらの公式ドキュメントに載っています。

--queueにキュー名、--urlにハンドラ(先程作成したcloud fucntions)のURLを記載します。
--body-contentに追加した値が、ハンドラのbodyで受け取れます。

$ gcloud tasks create-http-task \
--queue=testQueue1 \
--url=https://asia-northeast1-${PROJECT}.cloudfunctions.net/taskHandler1 \
--header=content-type:application/json \
--body-content='{"Name":"sensuikan1973", "Age":"100"}'

実行後のcloud functionのログです。実行されていることが分かり、bodyの内容も表示されています。

タスクの重複管理

タスクにはタスクIDを指定できます。タスクIDを指定するとタスクの重複管理が可能です。

// タスクIDをtaskid1とする
$ gcloud tasks create-http-task taskid1 \
--queue=testQueue1 \
--url=https://asia-northeast1-${PROJECT}.cloudfunctions.net/taskHandler1 \
--header=content-type:application/json \
--body-content='{"Name":"sensuikan1973", "Age":"100"}'

// 同様のタスクを追加する
$ gcloud tasks create-http-task taskid1 \
--queue=testQueue1 \
--url=https://asia-northeast1-${PROJECT}.cloudfunctions.net/taskHandler1 \
--header=content-type:application/json \
--body-content='{"Name":"sensuikan1973", "Age":"100"}'

// 重複タスクは追加できず、エラーとなる
ERROR: (gcloud.tasks.create-http-task) ALREADY_EXISTS: Requested entity already exists

重複管理には制約があるので注意が必要です。
例えばタスクが削除または実行されてから 1時間は、同じ名前の別のタスクを作成することができません。
追加しようとすると次のエラーが発生します。
ERROR: (gcloud.tasks.create-http-task) ALREADY_EXISTS: The task cannot be created because a task with this name existed too recently.

詳細はこちらのドキュメントを確認してみてください。
https://cloud.google.com/tasks/docs/reference/rest/v2/projects.locations.queues.tasks/create#request-body

リトライ処理

HTTPのハンドラを実行した場合、10分以内に200系のレスポンスを返さないと再試行が行われます。
https://cloud.google.com/tasks/docs/dual-overview?hl=ja#http

リトライ処理を確認するため、レスポンスコードを400を返すようにします。

index
exports.taskHandler1 = (req, res) => {
  console.info("body:", req.body)
  res.status(400).send("taskHandler1 called!!");
}

再度デプロイします。
gcloud functions deploy taskHandler1 --runtime=nodejs12 --trigger-http --region asia-northeast1

また、リトライ処理が分かりやすいようキューの設定を変更します。

$ gcloud tasks queues update testQueue1 \
--max-attempts 6 \
--min-backoff 5s \
--max-doublings 3

$ gcloud tasks queues describe testQueue1
rateLimits:
  maxBurstSize: 100
  maxConcurrentDispatches: 1000
  maxDispatchesPerSecond: 500.0
retryConfig:
  maxAttempts: 6
  maxBackoff: 3600s
  maxDoublings: 3
  minBackoff: 5s
state: RUNNING
// タスクを実行
$ gcloud tasks create-http-task \
--queue=testQueue1 \
--url=https://asia-northeast1-${PROJECT}.cloudfunctions.net/taskHandler1 \
--header=content-type:application/json \
--body-content='{"Name":"sensuikan1973", "Age":"100"}'

最大試行回数の6回実行されています。

5s -> 10s -> 20s -> 40s -> 80s の間隔で再実行が行われています。
これはmaxDoublings(最大倍数回数)を3に指定しているため、最初の3回は実行時間が2倍になり、その後はリトライ時間は前回分が加算されます。
今回の例が分かりづらかったですが、今後は次の間隔で再実行されてきます。

5s -> 10s -> 20s -> 40s -> 80s -> 160s -> 240s -> ...

https://www.pospome.work/entry/2017/07/09/153830

おわりに

GCPは最近触り始めたのですが、やってみた系の日本語ドキュメントはAWSに比べて少ないですね。
クラスメソッド developers.ioは偉大であることを認識できました。

Discussion