top pageへ.

Programming Room.

Google Cloud Tips.

GAE Tips.

Cloud Tasksの使い方.2020/08/06

 GAEからGoogle Cloud Tasksを使う方法の説明です。


おしながき

概要.

 Cloud Tasksは非同期でタスクを実行するための仕組みです。何らかのタスク実行要求が発生した時すぐにそのタスクを実行するのではなく、後からそのタスクを実行する仕組みです。UIなどのようになるべく早く結果を出す必要がある処理では後から実行というわけにはいきませんが、結果を後からまとめて確認できれば十分なケースは存在します。このようなケースで確実に要求されたタスクを実行するための仕組みです。

 GAEの第1世代VMに実装されていたタスクキューと同じようなものなので、タスクキューになれた方なら簡単に使えると思います。


ドキュメント.

 公式ドキュメントは以下のリンクから。


名称について.

 Cloud Tasksでは以下の名称が頻繁に登場します。

名称 フォーマット 扱うクラス
プロジェクト名 projects/PROJECT_ID -
ロケーション名 (リージョン名) projects/PROJECT_ID/locations/LOCATION_ID LocationName
キュー名 projects/PROJECT_ID/locations/LOCATION_ID/queues/QUEUE_ID QueueName
タスク名 projects/PROJECT_ID/locations/LOCATION_ID/queues/QUEUE_ID/tasks/TASK_ID TaskName

 これらはいずれも正しく上記のフォーマットになっていないと、認識してくれません。これらの名前を扱うクラスが用意されており、そのBuilderクラスは正しいフォーマットで文字列を生成してくれるので、それを使用するのが間違いないと思います。

 プロジェクト名・ロケーション名はCloud Schedulerでも使用しますが、扱うクラスはそれぞれのパッケージで用意されています。クラス名は同じでも実体は別物ですが、中身は同じと思われます。


準備.

ライブラリの組み込み.

 「Java用クライアントライブラリをインポートする」に記載されていますが、Mavenプロジェクトならpom.xmlのdependencies要素に下記を追記するだけで、Cloud Tasksのライブラリがプロジェクトに組み込まれます。

        <dependency>
           <groupId>com.google.cloud</groupId>
           <artifactId>google-cloud-tasks</artifactId>
        </dependency>

キューの管理.

 

 キューを管理する方法は、以下の2種類です。

方法 用途
gcloudコマンドを使用 最初にキューを作って作りっぱなしにするケースに適しています。
クライアントライブラリを使用 アプリ実行時にキューを動的に管理したいケースに適しています。

 

 今のところGCPコンソールではキューの停止/再開/削除は出来るものの、キューを作ることが出来ません。タスクも作成できず、未実行のタスクの削除と即時実行のみ出来ます。
 gcloudコマンドを使用するのなら「Cloud Tasksキューの作成」に説明されているとおりです。gcloudコマンドのヘルプを読みながらでも何とかなると思います。

 ここではクライアントライブラリを使用して、Javaコードでキューを管理する方法について説明します。


キューの生成.

 キューの生成にはqueue.xml/queue.yamlは使用しません。既存プロジェクトからの移行で既に使用している場合、これらは使用しないように変更が必要です。


 クライアントライブラリを使用するなら、下記のサンプルソースの様になります。公式ドキュメントなら「キューの作成」が参考になります。

        try (CloudTasksClient tasksclient = CloudTasksClient.create()) {
            RetryConfig.Builder cfgbuilder = RetryConfig.newBuilder()
             .setMaxAttempts(3);                                             // リトライ回数を3回にしてみた
            Queue.Builder quebuilder = Queue.newBuilder()
             .setName(QueueName.of("プロジェクトID", "リージョンID", "キューID").toString())
             .setRetryConfig(cfgbuilder);
            CreateQueueRequest.Builder reqbuilder = CreateQueueRequest.newBuilder()
             .setParent(LocationName.of("プロジェクトID", "リージョンID").toString())
             .setQueue(quebuilder);
            Queue queue= tasksclient.createQueue(reqbuilder.build());        // 生成されたキューの設定内容が返る
        }

 キューに対して実行したい内容に対応したメソッドがCloudTasksClientクラスに用意されており、それらのメソッドに渡すための引数となるクラスも同じパッケージの別クラスで用意されています。上記の様に、やたらなんでもBuilderで生成することになります。LocationName, QueueNameもBuilderがありますが、これらは名前を設定するだけなのでBuilderクラスよりof()メソッドでサクッと生成した方が手っ取り早いです。
 Queue.Builder.setName()メソッドに渡すキュー名や、CreateQueueRequest.Builder.setParent()メソッドに渡すロケーション名は、プロジェクトID・ロケーションID(リージョンID)を含んでいます。何度もプロジェクトID・ロケーションIDを指定するのも面倒ですが、指定しないとエラーになります。

 このサンプルではリトライ回数を3回に減らしてみました。タスクがエラー終了した場合3回までリトライするので、最大4回の試行される設定です。デフォルトでよければ、RetryConfigは必要なくなります。 


キューの削除.

 下記のサンプルソースの様になります。キュー名だけ指定すれば削除できるので、生成に比べれば簡単です。

        try (CloudTasksClient tasksclient = CloudTasksClient.create()) {
            DeleteQueueRequest.Builder reqbuilder = DeleteQueueRequest.newBuilder()
             .setName(QueueName.of("プロジェクトID", "リージョンID", "キューID").toString());
            tasksclient.deleteQueue(reqbuilder.build());
        }

 削除したキューと同名のキューは、削除から7日間は生成できません。この制限はGCPコンソール/gcloudコマンド/APIなどの削除方法によらず変わらないようです。
 指定したキューが存在しない場合、下記のような例外が発生します。

com.google.api.gax.rpc.NotFoundException: io.grpc.StatusRuntimeException: NOT_FOUND: Queue does not exist. If you just created the queue, wait at least a minute for the queue to initialize.

キューの取得.

 キューの設定内容は、下記の様にして取得できます。これもキュー名だけ指定すれば取得できます。

        try (CloudTasksClient tasksclient = CloudTasksClient.create()) {
            GetQueueRequest.Builder reqbuilder = GetQueueRequest.newBuilder()
             .setName(QueueName.of("プロジェクトID", "リージョンID", "キュー名").toString());
            Queue queue = tasksclient.getQueue(reqbuilder.build());        // キューの設定内容が返る
        }

 指定したキューが存在しない場合は、キューの削除と同様に例外が発生します。


キューのリスト.

 現在存在するキューのリストは、下記の様にして取得できます。キューの名前だけではなく、キューの設定内容のリストが得られます。

        try (CloudTasksClient tasksclient = CloudTasksClient.create()) {
            ListQueuesRequest.Builder reqbuilder = ListQueuesRequest.newBuilder()
             .setParent(LocationName.of("プロジェクトID", "リージョンID").toString());
            CloudTasksClient.ListQueuesPagedResponse queueslist = tasksclient.listQueues(reqbuilder.build());
            for (Queue queue: queueslist.iterateAll()) {
                queueに各キューの設定内容
            }
        }

 ListQueuesRequest.Builderクラスを操作すれば、フィルタリングも出来るようです。setFilter()メソッドにその例が載っています。
 ListQueuesRequest.BuilderクラスとCloudTasksClient.ListQueuesPagedResponseクラスを駆使すれば、キュー数が多くてもページング操作ができそうです。そんなに沢山キューを作ることもないと思いますが。


その他のキュー操作.

 その他に下記の操作が用意されています。これらは私もまだ動作を未確認なので、紹介だけ。

操作内容 実行メソッド リクエストクラス
キューの一時停止 CloudTasksClient.pauseQueue PauseQueueRequest
キューの再開 CloudTasksClient.resumeQueue ResumeQueueRequest
キュー内のタスク破棄 CloudTasksClient.purgeQueue PurgeQueueRequest
キューの設定内容の更新 CloudTasksClient.updateQueue UpdateQueueRequest

タスクの管理.

タスクの生成.

 以下は、GAEに実装したリクエストエンドポイントをたたくタスクを生成するサンプルソースです。

        // リクエストボディーの生成
        String body = "Hello world.\n"    // リクエストのボディは適当

        // App Engineタスクの生成
        Task.Builder tskbuilder = Task.newBuilder()
         .setAppEngineHttpRequest(
            AppEngineHttpRequest.newBuilder()
             .setRelativeUri("リクエストエンドポイントのリラティブURI")
             .setHttpMethod(HttpMethod.POST)                               // メソッドはPOSTを指定
             .setBody(ByteString.copyFromUtf8(body))
             .build()
         );
        // スケジュールの設定
        Date schedule = スケジュール日時; 
        if (schedule != null) {
            Timestamp.Builder timbuilder = Timestamp.newBuilder().setSeconds(schedule.getTime() /1000).setNanos(0);
            tskbuilder.setScheduleTime(timbuilder);
        }

        try (CloudTasksClient tasksclient = CloudTasksClient.create()) {
            // タスクの生成
            CreateTaskRequest.Builder reqbuilder = CreateTaskRequest.newBuilder()
             .setParent(QueueName.of("プロジェクトID", "リージョンID", "キューID").toString())
             .setTask(tskbuilder.build());
            Task task = tasksclient.createTask(reqbuilder.build());            // 生成されたタスクの設定内容が返る
        }

 キューと同様に、タスクに対しても実行したい内容に対応したメソッドがCloudTasksClientクラスに用意されており、それらのメソッドに渡すための引数となるクラスも同じパッケージの別クラスで用意されています。キュー同様にタスクも、やたらBuilderだらけになります。
 Builderクラスよりof()メソッドの方が簡単に使えるのも、キューの場合と同じです。タスクの場合はLocationName, QueueNameに加えてさらにTaskNameを使用します。

 上記のサンプルではTask.Builder.setScheduleTime()メソッドを使ってタスクの実行日時を指定しています。すぐに実行すればよい場合は、これは必要ありません。

 このサンプルではタスク名を指定していません。タスク名を指定しない場合は、フレームワークがランダムでユニークなタスク名を付けます。自分でタスク名を付けたい場合はTask.Builder.setName()メソッドで付けられます。「REST リファレンス」によると、以前の同名タスクが実行または削除されてから約1時間以上たたないと、タスク生成できないようです。1時間以内だと以下のような例外が発生します。『約1時間』なので、ちょうど1時間だとまだ例外発生することがありました。

com.google.api.gax.rpc.AlreadyExistsException: io.grpc.StatusRuntimeException: ALREADY_EXISTS: The task cannot be created because a task with this name existed too recently. For more information about task de-duplication see https://cloud.google.com/tasks/docs/reference/rest/v2/projects.locations.queues.tasks/create#body.request_body.FIELDS.task.

タスクの削除.

 タスク名を指定して、そのタスクを削除することができます。以下はそのサンプルソースです。

        try (CloudTasksClient tasksclient = CloudTasksClient.create()) {
            DeleteTaskRequest.Builder reqbuilder = DeleteTaskRequest.newBuilder()
             .setName(TaskName.of("プロジェクトID", "リージョンID", "キューID", "タスクID").toString());
            tasksclient.deleteTask(reqbuilder.build());
        }

 指定したタスクが存在しない場合は、以下のような例外が発生します。キューが見つからなかった場合とクラスは同じですが、メッセージが異なります。

com.google.api.gax.rpc.NotFoundException: io.grpc.StatusRuntimeException: NOT_FOUND: Requested entity was not found.

タスクの取得.

 タスクの設定内容は、以下の様にして取得できます。

        try (CloudTasksClient tasksclient = CloudTasksClient.create()) {
            GetTaskRequest.Builder reqbuilder = GetTaskRequest.newBuilder()
             .setName(TaskName.of("プロジェクトID", "リージョンID", "キューID", "タスクID").toString());
            Task task = tasksclient.getTask(reqbuilder.build());        // タスクの設定内容が返る
        }

 指定したタスクが存在しない場合は、タスクの削除と同様に例外が発生します。


タスクのリスト.

 指定のキュー内に現在存在するタスクのリストは、下記の様にして取得できます。タスクの名前だけではなく、タスクの設定内容のリストが得られます。

        try (CloudTasksClient tasksclient = CloudTasksClient.create()) {
            ListTasksRequest.Builder reqbuilder = ListTasksRequest.newBuilder()
             .setParent(QueueName.of("プロジェクトID", "リージョンID", "キューID").toString());
            for (Task task: tasksclient.listTasks(reqbuilder.build()).iterateAll()) {
                taskに各タスクの設定内容
            }
        }

 キューと違って、フィルタリングの機能は提供されないようです。ListTasksRequest.Builderクラスにそれらしいメソッドが見つかりません。
 ListTasksRequest.BuilderクラスとCloudTasksClient.ListTasksPagedResponsクラスを駆使すれば、タスク数が多くてもページング操作ができそうです。キューと同様ですね。


その他のタスク操作.

 その他に用意されているタスク操作は、あれ、これだけ。動作未確認なので、紹介だけです。

操作内容 実行メソッド リクエストクラス
タスクの即時実行 CloudTasksClient.runTask RunTaskRequest

最後に.

 公式ドキュメントには説明が少なすぎて、いろいろと不明点だらけなので、ある程度調べた結果を公開してみました。特にクライアントライブラリのAPIリファレンスはクラスによってはほとんど説明がなかったりと、不毛地帯になっています。Googleさん、早く耕して苗を植えて実らせてください。クライアントライブラリで説明しきれていない事柄については、REST APIから相当のものを探せば参考になると思います。


▲page top.
Copyright 2005-2024, yosshie.