コンテンツにスキップ

Pub/Sub を触ってみた

Google Cloud の Pub/Sub で、メッセージの受け取り方である Push・Pull・BigQuery の違いと、ack による処理完了の通知を整理する。Pub/Sub は、送信側と受信側を直接つながずにメッセージを受け渡すサービスだ。

概要

公式ドキュメントから

データを取り込んで配布するためのストリーミング分析とデータ統合パイプラインに使用されます。これは、サービスの統合を目的としたメッセージング指向のミドルウェア、または、タスクを同時に読み込むキューとしても使用されます。

この記事では、Push、Pull、BigQuery の 3 種類のサブスクリプションを扱う。それぞれのユースケースについて書かれたPub/Sub のサブスクリプションのドキュメントがある。

Push 型の Subscription にどの ServiceAccount を関連付けるかを設定できる。関連付けた ServiceAccount の権限で、Cloud Run 等の認証設定を行う。確認応答期限はタイムアウトのようなもので、サブスクライバーからその期限内に応答がなければ再試行ポリシーに則って再送する。再試行ポリシーは次の 2 つから選べる。

  • すぐに再送する
  • 指数バックオフの再送をする

メッセージはpublish_timeの情報を持っており、再送されてもpublish_timeの値は変わらない。

デフォルトでは最低 1 回の配信保証となっており、場合によっては 2 回以上配信されることもある。執筆時点(2022 年 10 月)では、1 回限りの配信を有効化するオプションもプレビューとして利用できた。なお、Pub/Sub のリリースノートは、このオプションが 2022 年 12 月 1 日付で GA になったことを告知している。

Pull 型の Subscription

Pull 型には StreamingPull と単項 Pull の2 通りがあり、どちらの方式も ack を返すことで処理の完了を伝える。それぞれの特徴と選択する場面を次の表にまとめた。

方式 特徴 選択する場面
StreamingPull スループットが比較的高い レイテンシを最小化したい場合
単項 Pull 処理するメッセージ数を制限できる メモリやリソースに不安があり、流量を制限したい場合

扱うメッセージ処理には 2 種類あり、非同期 Pull と同期 Pull がある。リソースの状況に制約がある場合は単項 Pull API と同期モードで処理を行う。

StreamingPull flow control

flow control を使うと StreamingPull で一度に受信できるメッセージ数をコントロールできる。

def receive_messages_with_flow_control(
    project_id: str, subscription_id: str, timeout: Optional[float] = None
) -> None:
    """Receives messages from a pull subscription with flow control."""
    # [START pubsub_subscriber_flow_settings]
    from concurrent.futures import TimeoutError
    from google.cloud import pubsub_v1

    # TODO(developer)
    # project_id = "your-project-id"
    # subscription_id = "your-subscription-id"
    # Number of seconds the subscriber should listen for messages
    # timeout = 5.0

    subscriber = pubsub_v1.SubscriberClient()
    subscription_path = subscriber.subscription_path(project_id, subscription_id)

    def callback(message: pubsub_v1.subscriber.message.Message) -> None:
        print(f"Received {message.data!r}.")
        message.ack()

    # Limit the subscriber to only have ten outstanding messages at a time.
    flow_control = pubsub_v1.types.FlowControl(max_messages=10)

    streaming_pull_future = subscriber.subscribe(
        subscription_path, callback=callback, flow_control=flow_control
    )
    print(f"Listening for messages on {subscription_path}..\n")

    # Wrap subscriber in a 'with' block to automatically call close() when done.
    with subscriber:
        try:
            # When `timeout` is not set, result() will block indefinitely,
            # unless an exception is encountered first.
            streaming_pull_future.result(timeout=timeout)
        except TimeoutError:
            streaming_pull_future.cancel()  # Trigger the shutdown.
            streaming_pull_future.result()  # Block until the shutdown is complete.
    # [END pubsub_subscriber_flow_settings]

別プロジェクトの Topic と Subscription を関連付ける

プロジェクトをまたぐ Topic と Subscription を関連付けるには Subscription 作成時に Topic を指定し、Pub/Sub Subscriberの権限が必要になる。

1
2
3
4
5
gcloud pubsub subscriptions create project_a_subscription \
    --topic=project_b_topic \
    --topic-project=project_b \
    --push-auth-service-account=foo@project_a.iam.gserviceaccount.com \
    --push-endpoint=https://example.com

Subscription の有効期限

Subscription に有効期限を設けることができる。一時的に作成して消すのを忘れそうな時などに使う。

システムテスト

Cloud Functions の公式サンプルが、システムテストを行う例を紹介している。出力ログを確認すると、Pub/Sub がメッセージを配信したとわかる。

Pub/Sub の指標を使った Compute Engine のスケール

Google Cloud の Stackdriver per-group metrics の記事は、Pub/Sub の指標を使った Compute Engine のスケール設定を紹介している。マネージドインスタンスグループを作成する際にスケールの指標を Pub/Sub の指標に設定すると、メッセージの数に応じて Compute Engine がスケールする。 Pub/Sub の指標以外にも CPU 使用率などの指標を利用できる。複数の指標を組み合わせてスケールする場合は、最大の数になるようにスケールする。

まとめ

Subscription のタイプや再試行ポリシー、配信保証の設定によって、Pub/Sub の挙動は変わる。デフォルトの配信保証は最低 1 回で、同じメッセージが 2 回以上届く場合もある。そのため、単に購読するだけでなく、再試行ポリシーや ack のタイミングを理解しておくとよい。StreamingPull の flow control を使えば一度に受信するメッセージ数を制限でき、リソースに制約がある場合は単項 Pull と同期モードも選べる。別プロジェクトの Topic との関連付けや、Pub/Sub の指標を使った Compute Engine のスケールなど、周辺システムとの組み合わせもあわせて設計すると運用しやすい。