前提知識
サーバーレスに移行したい
NestJSやってると、タスクキューはbullやscheduleなのですが、インスタンス立ち上げっぱなしな制約を受けてしまうため、なかなかサーバーレスな環境に移行するのが難しいものがあります。
今回、サーバーレスな環境に移行しようという状況が出てきました。「どうにかしてbullとscheduleの使用をやめよう」と作業をしていて、代替えの技術となったのがCloud RunとCloud Pub/Subの組み合わせでした。
Pub/Sub経由でHTTPリクエストをしてもらい、NestJSを起動し、リクエスト内容によって処理(Provider)を分けることがゴールになります。
その手助けをしてくれるライブラリを@anchan828/nest-cloud-run-pubsubとして作りました。
PublisherとSubscriberの2つのアプリを作成する
意味合い的にはメッセージを発行するためのパブリッシャー、受け取り側のサブスクライバーが存在します。なので2つのアプリケーションを作らないといけないですが、コード的には1つのアプリ(Cloud Runなので1つのDockerイメージ)で問題ないです。Cloud Runサービスを2つ作ればいいだけです。たぶん1つのサービスでも行けると思いますがやったことがない...
受け手側のWorkerを作成する
イメージしやすいように、先にリクエストの受け取り側サブスクライバーの説明をします。
@anchan828/nest-cloud-run-pubsubは、リクエスト内容(リクエストボディ)によって呼び出すProviderを決めます。なのでPub/Subのトピックに送るメッセージ内容で呼び出すWorkerが変わります。これにより、bullのようなProcessorの書き方ができます。
@CloudRunPubSubWorker("ワーカー名")
class Worker {
@CloudRunPubSubWorkerProcess()
public async process(message: string): Promise<void> {
console.log("メッセージ内容: " + message);
}
}
メッセージを送信する
次にデータの送信側です。CloudRunPubSubServiceという@google-cloud/pubsubのラッパーサービスを用意していて、publish関数で指定のワーカーとメッセージをトピックに送信します。あとはワーカーが動いているCloud Runのサービスに向けてHTTPリクエストを投げるようにサブスクリプションをセットアップするだけです。
export class Service {
constructor(private readonly pubsubService: CloudRunPubSubService) {}
public async sendMessage(): Promise<void> {
await this.pubsubService.publish({
name: "ワーカー名",
data: "メッセージ",
});
}
}
まとめ
これで対応終わりです。
...NestJS側の対応は簡単なのですが、初めてセットアップをやるとき、Cloud Runの設定やPub/Subのサービスアカウント周りの権限など躓くところが多いと思います。そこさえクリアできれば、快適な環境が作れると思いますので、GCPを使ってるNestJSユーザーは、ぜひ Cloud Run + Pub/Sub の組み合わせを検討してみてください。