GAEからGoogle Cloud Schedulerを使う方法の説明です。
おしながき
概要.
Cloud Schedulerは定期的にジョブを実行するための仕組みです。たぶんunixの世界で言うcronそのものです。GCPで提供されるサービスを定期的に実行することが出来るようになります。
GAEの第1世代VMに実装されていたcronと同じようなものなので、cronになれた方なら簡単に使えると思います。
「サポートされるリージョン」には、なぜかプロジェクトにはGAEアプリが必要と記載されています。今回はGAEアプリからCloud Schedulerを使用するので、この点は問題になりません。
Cloud Schedulerは2019/06に利用可能になったばかりで、公式ドキュメントを読んでもgcloudコマンドを使用してジョブを制御する方法しか記載されていません。当然、世のアプリケーションはそんな『手作業で設定してハイ終わり』なものばかりではないどころか、そんな単純なケースの方が少数派だと思われます。私もAPIを使ってプログラムでジョブを制御したい方なのですが、結局参考になるものは現時点ではAPIリファレンスしか見つかりませんでした。
というわけでこのページで説明する内容が正しいのか確証がありませんが、結果的にCloud Tasksと同じような感じて使えることが分かったこと、お試し実装したアプリが期待どおりに動作したことから、ある程度参考にしていただけるものと思います。
ドキュメント.
公式ドキュメントは以下のリンクから。
名称について.
Cloud Schedulerでは以下の名称が頻繁に登場します。
名称 | フォーマット | 扱うクラス |
---|---|---|
プロジェクト名 | projects/PROJECT_ID | - |
ロケーション名 (リージョン名) | projects/PROJECT_ID/locations/LOCATION_ID | LocationName |
ジョブ名 | projects/PROJECT_ID/locations/LOCATION_ID/jobs/JOB_ID | JobName |
これらはいずれも正しく上記のフォーマットになっていないと、認識してくれません。これらの名前を扱うクラスが用意されており、そのBuilderクラスは正しいフォーマットで文字列を生成してくれるので、それを使用するのが間違いないと思います。
プロジェクト名・ロケーション名はCloud Tasksでも使用しますが、扱うクラスはそれぞれのパッケージで用意されています。クラス名は同じでも実体は別物ですが、中身は同じと思われます。
準備.
ライブラリの組み込み.
クライアントライブラリの組み込み方法に関しては、公式ドキュメントに記載は見つかりませんでした。MVN REPOSITORYを探して見つけました。Mavenプロジェクトならpom.xmlのdependencies要素に下記を追記することで、Cloud Schedulerのライブラリがプロジェクトに組み込まれました。
<!-- Cloud Scheduler --> <!-- https://mvnrepository.com/artifact/com.google.api.grpc/grpc-google-cloud-scheduler-v1 --> <dependency> <groupId>com.google.cloud</groupId> <artifactId>google-cloud-scheduler</artifactId> </dependency>
ジョブの管理.
ジョブを管理する方法は、以下の3種類です。
方法 | 用途 |
---|---|
gcloudコマンドを使用 |
|
GCPコンソールを使用 |
|
クライアントライブラリを使用 |
|
gcloudコマンドおよびGCPコンソールを使用するのなら「ジョブの作成」に説明されているとおりです。ここではクライアントライブラリを使用して、Javaコードでジョブを管理する方法について説明します。
ジョブの生成.
ジョブの生成にはcron.xml/cron.yamlは使用しません。既存プロジェクトからの移行で既に使用している場合、これらは使用しないように変更が必要です。
クライアントライブラリを使用するなら、下記のサンプルソースの様になります。
// リクエストボディーの生成 String body = "Hello world.\n" // リクエストのボディは適当 // App Engineターゲットの生成 AppEngineHttpTarget.Builder gaebuilder = AppEngineHttpTarget.newBuilder() .setHttpMethod(HttpMethod.POST) .setRelativeUri("リクエストエンドポイントのリラティブURI") .setBody(ByteString.copyFromUtf8(body)); // リトライ設定の生成 RetryConfig.Builder retrybuilder = RetryConfig.newBuilder().setRetryCount(3); // リトライ回数を3回にしてみた try (CloudSchedulerClient schedulerclient = CloudSchedulerClient.create()) { // App Engineジョブの生成 Job.Builder jobbuilder = Job.newBuilder() .setName(JobName.of("プロジェクトID", "リージョンID", "ジョブID").toString()) .setAppEngineHttpTarget(gaebuilder) // App Engineターゲットを設定 .setTimeZone("Asia/Tokyo") // タイムゾーンは東京を指定 .setSchedule(schedule) // スケジュールを指定 .setRetryConfig(retrybuilder.build()); CreateJobRequest.Builder reqbuilder = CreateJobRequest.newBuilder() .setParent(LocationName.of("プロジェクトID", "リージョンID").toString()) .setJob(jobbuilder.build()); Job job = schedulerclient.createJob(reqbuilder.build()); // 生成されたジョブの設定内容が返る }
ジョブに対して実行したい内容に対応したメソッドがCloudSchedulerClientクラスに用意されており、それらのメソッドに渡すための引数となるクラスも同じパッケージの別クラスで用意されています。やたらBuilderで生成するのはCloud
Tasksと同じです。LocationName, JobNameもBuilderがありますが、これらは名前を設定するだけなのでBuilderクラスよりof()メソッドでサクッと生成した方が手っ取り早いです。
Job.Builder.setName()メソッドに渡すジョブ名や、CreateJobRequest.Builder.setParent()メソッドに渡すロケーション名は、プロジェクトID・ロケーションID(リージョンID)を含んでいます。何度もプロジェクトID・ロケーションIDを指定するのも面倒ですが、指定しないとエラーになります。
このサンプルではリトライ回数を3回に減らしてみました。ジョブがエラー終了した場合3回までリトライするので、最大4回の試行される設定です。デフォルトでよければ、RetryConfigは必要なくなります。
アプリが日本でしか使われないなら、タイムゾーンには東京を指定します。指定しないとUTCが使われます。
スケジュールはJob.Builder.setSchedule()メソッドで指定します。このメソッドの引数は「ジョブ スケジュールの定義」に説明されているとおりのunix-cron形式の文字列です。5分毎なら"*/5 * * * *"となります。
ジョブの削除.
下記のサンプルソースの様になります。ジョブ名だけ指定すれば削除できるので、生成に比べれば簡単です。
try (CloudSchedulerClient schedulerclient = CloudSchedulerClient.create()) { DeleteJobRequest.Builder reqbuilder = DeleteJobRequest.newBuilder() .setName(JobName.of("プロジェクトID", "リージョンID", "ジョブID").toString()); schedulerclient.deleteJob(reqbuilder.build()); }
指定したジョブが存在しない場合、下記のような例外が発生します。
com.google.api.gax.rpc.NotFoundException: io.grpc.StatusRuntimeException: NOT_FOUND: Job not found.
ジョブの取得.
ジョブの設定内容は、下記の様にして取得できます。これもジョブ名だけ指定すれば取得できます。
try (CloudSchedulerClient schedulerclient = CloudSchedulerClient.create()) { GetJobRequest.Builder reqbuilder = GetJobRequest.newBuilder() .setName(JobName.of("プロジェクトID", "リージョンID", "ジョブID").toString()); Job job = schedulerclient.getJob(reqbuilder.build()); // ジョブの設定内容が返る }
指定したジョブが存在しない場合は、ジョブの削除と同様に例外が発生します。
ジョブのリスト.
現在存在するジョブのリストは、下記の様にして取得できます。ジョブの名前だけではなく、ジョブの設定内容のリストが得られます。
try (CloudSchedulerClient schedulerclient = CloudSchedulerClient.create()) { ListJobsRequest.Builder reqbuilder = ListJobsRequest.newBuilder() .setParent(LocationName.of("プロジェクトID", "リージョンID").toString()); for (Job job: schedulerclient.listJobs(reqbuilder.build()).iterateAll()) { jobに各ジョブの設定内容 } }
ListJobsRequest.BuilderクラスとCloudSchedulerClient.ListJobsPagedResponseクラスを駆使すれば、ジョブ数が多くてもページング操作ができそうです。
その他のジョブ操作.
その他に、下記の操作が用意されています。CloudSchedulerClient.updateJob()以外は、どれも操作対象のジョブを指定するだけなので、ジョブの削除・取得と同様のコードで実装できます。引数にジョブ名をとるメソッドを使用すれば、リクエストクラスのインスタンスをBuilderで作る手間も省けます。
操作内容 | 実行メソッド | リクエストクラス | 説明 |
---|---|---|---|
ジョブの一時停止 | CloudSchedulerClient.pauseJob() | PauseJobRequest | 実行中のジョブを一時停止します。既に停止中のジョブには無効果で、例外も発生しません。 |
ジョブの再開 | CloudSchedulerClient.resumeJob() | ResumeJobRequest | 一時停止中のジョブを実行再開します。実行中のジョブには無効果で、例外も発生しません。 |
ジョブの即時実行 | CloudSchedulerClient.runJob() | RunJobRequest | ジョブを即時実行します。その後のスケジュールされた実行に影響はありません。一時停止中のジョブに対して行うと、例外発生します。 |
ジョブの設定内容の更新 | CloudSchedulerClient.updateJob() | UpdateJobRequest | ジョブの設定内容を更新できるようです。 残念ながら動作未確認。 |
CloudSchedulerClient.runJob()を一時停止中のジョブに対して行った場合に発生する例外は、以下のとおりでした。
com.google.api.gax.rpc.FailedPreconditionException: io.grpc.StatusRuntimeException: FAILED_PRECONDITION: Job.state must be ENABLED for RunJob.
最後に.
公式ドキュメントは本当に『準備中』といった雰囲気です。これでリリースとは酷いものですね。「どうやって使えというんだ」と思いました。
Copyright 2005-2024, yosshie.