Pub/Sub のアーキテクチャの概要

Pub/Sub は、信頼性とスケーラビリティに優れた非同期メッセージ サービスです。このサービスは、さまざまな Google サービスが 10 年以上にわたって基盤としてきた中核的な Google インフラストラクチャ コンポーネントをベースにしています。Google 広告や Google 検索、Gmail などの Google サービスは、このインフラストラクチャを使用して、1 秒あたり 5 億件以上のメッセージ、総計 1 TB/秒以上のデータを送信しています。ここでは、Pub/Sub がこの水準の規模の信頼性を実現するために備えている設計上の主な特長について説明します。

メッセージ サービスのパフォーマンスを判断する

Pub/Sub のようなメッセージ サービスでは、スケーラビリティ、可用性、レイテンシという 3 つの要素がパフォーマンスの判断基準となります。この 3 要素は互いに相いれないことが多く、2 つの要素を高めるために 1 つの要素を犠牲にしなければならないことがあります。

「スケーラビリティ」、「可用性」、「レイテンシ」という用語は、システムのさまざまなプロパティを指す場合があるため、以下のセクションでは Pub/Sub での定義について説明します。

スケーラビリティ

スケーラブルなサービスは、レイテンシや可用性を大幅に損なうことなく、負荷の増大に対処できます。ここで言う「負荷」とは、次のような Pub/Sub の利用状況に関するさまざまな要素のことを指します。

  • トピックの数

  • パブリッシャーの数

  • サブスクリプションの数

  • サブスクライバーの数

  • メッセージの数

  • メッセージのサイズ

  • メッセージのパブリッシュ レートやコンシューム レート(スループット)

  • 特定のサブスクリプションのバックログのサイズ

可用性

分散システムでは、発生し得る問題の種類や深刻度に大きな幅があります。システムの可用性は、さまざまな種類の問題に対してどれくらい適切に対処できるか、エンドユーザーに気づかれないように適切にフェイルオーバーできるか、という観点で測定されます。障害はハードウェアで発生する場合があり(ディスク ドライブの不調ネットワーク接続の問題など)、さらにソフトウェアで障害が発生する場合や、負荷に起因して障害が発生する場合もあります。負荷に起因する障害は、たとえば、サービス内(あるいは、同一ハードウェア上で動作しているソフトウェア コンポーネント内や、ソフトウェアの依存関係内)でトラフィックが突然増加し、リソースが足りなくなったときなどに発生します。また、人的エラーによって可用性が低下することもあります(ソフトウェアや設定のビルド時、デプロイ時のミスなど)。

レイテンシ

レイテンシは、時間の面からシステムのパフォーマンスを測る尺度です。一般的に、サービスでは可能な限りレイテンシを最小限に抑えることが望まれます。Pub/Sub の場合、最も重要なレイテンシ指標は次の 2 つです。

  1. パブリッシュされたメッセージの確認応答を返信するまでにかかる時間。

  2. パブリッシュされたメッセージをサブスクライバーに配信するまでにかかる時間。

Pub/Sub の基本アーキテクチャ

このセクションでは、Pub/Sub の設計について説明し、このサービスがいかにして可用性を維持しつつ、スケーラビリティと低レイテンシを実現しているかについて説明します。 このシステムは、水平スケーリングに対応するよう設計されており、トピックの数やサブスクリプションの数、メッセージの数が増加したときには、稼働サーバーのインスタンスの数を増やすことで対処できるようになっています。

Pub/Sub サーバーは、世界中のすべての Google Cloud リージョンで稼働しています。これにより、メッセージの保存先をユーザーが制御しつつ、高速でグローバルなデータアクセスを提供できます。Cloud Pub/Sub では、パブリッシャーとサブスクライバーのクライアントが接続先のサーバーやデータのルーティング方法を意識しない、グローバルなデータアクセスを提供します。

Pub/Sub の負荷分散メカニズムは、IAM と管理コンソールリソース ロケーションの制限セクションで定義されているように、データ ストレージが許可されている最も近い Google Cloud データセンターにパブリッシャー トラフィックを振り向けます。 つまり、複数のリージョンのパブリッシャーが、低レイテンシの単一のトピックにメッセージをパブリッシュできます。個別のメッセージは、単一のリージョンに保存されます。ただし、トピックのメッセージが多数のリージョンに保存される場合もあります。サブスクライバー クライアントはこのトピックにパブリッシュされたメッセージをリクエストすると、クライアントへの配信のためにトピックにパブリッシュされたすべてのメッセージのデータを集約する、最も近いサーバーに接続します。

Pub/Sub は、データプレーンとコントロール プレーンという 2 つの主要部分に分割されます。データプレーンは、パブリッシャーとサブスクライバーの間で移動するメッセージを処理します。コントロール プレーンは、データプレーン上のサーバーに対するパブリッシャーとサブスクライバーの割り当てを処理します。データプレーン内のサーバーは「フォワーダー」と呼ばれ、コントロール プレーン内のサーバーは「ルーター」と呼ばれます。パブリッシャーとサブスクライバーが、割り当てられたフォワーダーに接続すると、フォワーダーにアクセス可能な限り、ルーターからの情報は一切不要になります。そのため、すでに接続済みでメッセージの送信や受信を行っているクライアントに影響を与えることなく、Pub/Sub のコントロール プレーンをアップグレードできます。

コントロール プレーン

Pub/Sub コントロール プレーンは、すべてのクライアントを対象としてスケーラビリティ、可用性、低レイテンシを実現するように、クライアントをフォワーダーに分配します。フォワーダーは、クライアントにトピックやサブスクリプションのサービスを提供します。クライアントが Pub/Sub に接続すると、ルーターが、ネットワーク距離を最短にするようにクライアントの接続先データセンターを決定します。ネットワーク距離とは、2 点間の接続に関するレイテンシの尺度のことを指します。ルーターは、データセンター内で利用可能なフォワーダー セットに全体の負荷を分散するように試みます。この割り当てを行う際、ルーターは、(a)負荷の均一性(各フォワーダーの負荷が均一になるのが理想)、(b)割り当ての安定性(負荷や利用可能なフォワーダー セットに変動があっても、できる限り既存の割り当てを変更しないようにするのが理想)という 2 つの目標の間でバランスを取る必要があります。ルーターでは、Google Research が開発したコンシステント ハッシュ法のバリエーションを使用して整合性と均一性のバランスを調整できます。ルーターは、クライアントに対して、接続先の候補となるフォワーダーの順序付きリストを提供します。この順序付きリストは、利用可能なフォワーダーの状況やクライアントからの負荷の状態に応じて変化する場合があります。

クライアントは、このフォワーダー リストを受け取り、1 つ以上のフォワーダーに接続します。クライアントは、ルーターが提示した順序に基づいてフォワーダーに接続しようとしますが、障害が発生した場合は、その点を考慮に入れます。たとえば、最も近いフォワーダーに接続しようとして何度か失敗した場合、別のデータセンター内にあるフォワーダーを試すことがあります。このような実装上の細部から Pub/Sub クライアントを解放するために、クライアントとフォワーダの間にサービス プロキシがあり、クライアントに代わってこの接続を最適化します。

データプレーン - メッセージの一生

データプレーンは、クライアントからメッセージを受け取り、クライアントにメッセージを送信します。Pub/Sub のデータプレーンを理解する最善の方法は、メッセージがサービスに受信された時点からサービス内に存在しなくなる時点までのメッセージの一生を確認することです。メッセージ処理のステップを順番に見ていきましょう。このセクションの説明では、メッセージをパブリッシュするトピックには、少なくとも 1 つのサブスクリプションが属するものとします。一般的に、メッセージは次のステップで処理されます。

  1. パブリッシャーがメッセージを送信します。

  2. メッセージがストレージに書き込まれます。

  3. Pub/Sub が、メッセージを受信したことを示す確認応答をパブリッシャーに返信し、トピックに属するすべてのサブスクリプションに配信することを保証します。

  4. メッセージをストレージに書き込むと同時に、Pub/Sub は、サブスクライバーにメッセージを配信します。

  5. サブスクライバーは、メッセージを処理したことを示す確認応答を Pub/Sub に返信します。

  6. 各サブスクリプションの少なくとも 1 つのサブスクライバーがメッセージ受信の確認応答を返信すると、Pub/Sub は、ストレージからメッセージを削除します。

まず、パブリッシャーがトピックに関するメッセージを Pub/Sub に送信します。メッセージは、プロキシレイヤによって暗号化され、パブリッシング フォワーダー(パブリッシャーが接続しているフォワーダー)に送信されます。確実に配信するため、メッセージはすぐにストレージに書き込まれます。フォワーダーは、最初にメッセージを N 個のクラスタ(N は奇数)に書き込みます。少なくとも ⌈N÷2⌉ 個のクラスタに書き込まれたときに、メッセージが永続したものとみなされます。メッセージが永続すると、パブリッシング フォワーダーは、メッセージ受信の確認応答をパブリッシャーに返信します。この段階で、Pub/Sub は、トピックに属するすべてのサブスクリプションにメッセージを配信することを保証します。バックグラウンド プロセスが N 個のクラスタすべてにメッセージがあるか定期的に確認し、メッセージのないクラスタがあった場合、そのクラスタにメッセージを書き込みます。

各クラスタ内で、M 個のディスク(M は奇数)にメッセージが書き込まれます。データが ⌈M÷2⌉ 個のディスクに書き込まれたときに、そのクラスタ内で永続したものとみなされます。総計で、パブリッシュされたメッセージは、少なくとも ⌈N÷2⌉ 個のクラスタの ⌈M÷2⌉ 個のディスクに書き込まれたときに永続したものとみなされ、最終的に N×M 個のディスクにレプリケーションされます。

パブリッシング フォワーダーには、トピックに属するすべてのサブスクリプションのリストがあります。パブリッシング フォワーダーには、パブリッシュされたメッセージと、各サブスクリプションによってどのメッセージの確認応答が返信されたのかを示すメタデータの両方を永続させる役割があります。特定のトピックを対象にパブリッシング フォワーダーによって受信と保存が行われたメッセージ セットは、この確認応答済みメッセージのトラッキング情報と合わせて、「パブリッシュ メッセージ ソース」と呼ばれます。トピックのスループット要件によっては、単一のパブリッシャーが複数のパブリッシング フォワーダーにメッセージを送信し、複数のパブリッシュ メッセージ ソースにメッセージを保存することがあります。また、同一トピックを対象に、異なるパブリッシャーがそれぞれ異なるパブリッシング フォワーダーにメッセージを送信する場合もあります。メッセージは単一のパブリッシング フォワーダーにのみ送信されます。Pub/Sub は、スループットの変化に応じて、特定のトピックのメッセージを受信するパブリッシング フォワーダーの数を動的に調整します。

サブスクライバーは、サブスクライビング フォワーダー(パブリッシャーからサブスクライバーまでメッセージが移動する際に通過するフォワーダー)に接続することで、メッセージを受信します。pull サブスクライバーの場合の「接続」とは、pull リクエストを発行することを意味します。push サブスクライバーの場合の「接続」とは、push エンドポイントを Pub/Sub に登録することを意味します。サブスクリプションが作成されると、そのポイント以降にパブリッシュされたすべてのメッセージがそのサブスクリプションに配信されることが保証されます。この仕組みは、同期ポイント保証と呼ばれます。

各サブスクライビング フォワーダーは、対象トピックのパブリッシュ メッセージ ソースを持っているパブリッシング フォワーダーのメッセージをリクエストする必要があります。パブリッシャーと同様、サブスクライバーも、複数のサブスクライビング フォワーダーに接続してメッセージを受信することがあります。これにより、すべてのサブスクライビング フォワーダーが、トピックのすべての公開メッセージ ソースにあるメッセージを認識または受信する必要がなくなり、Pub/Sub の水平スケーリングが可能になります。Pub/Sub は、サブスクライバーに配信されるメッセージのスループットに基いて、サブスクライビング フォワーダーの数を動的に調整します。サブスクライバーは、サブスクライビング フォワーダーを介して、スループットの変化に応じて特定のトピックのメッセージを受信します。

サブスクライビング フォワーダーは、対象トピックのパブリッシュ メッセージ ソースを持つ 1 つ以上のパブリッシング フォワーダーに対して、必要なメッセージを送信するようにリクエストします。パブリッシング フォワーダーは、確認応答済みではないメッセージをサブスクライビング フォワーダーに送信します。サブスクライビング フォワーダーは、そのメッセージをサブスクライバーにリレーします。

サブスクライバーは、メッセージを処理すると、サブスクライビング フォワーダーに確認応答を返信します。サブスクライビング フォワーダーは、この確認応答をパブリッシング フォワーダーにリレーします。パブリッシング フォワーダーは、確認応答をパブリッシュ メッセージ ソースに保存します。対象トピックのすべてのサブスクリプションがメッセージ受信の確認応答を返信すると、そのメッセージはパブリッシュ メッセージ ソースとストレージから非同期的に削除されます。

1 つのトピックとサブスクリプションに対して、さまざまなメッセージが、さまざまなパブリッシャー、サブスクライバー、パブリッシング フォワーダー、サブスクライビング フォワーダーを経由して流れる可能性があります。パブリッシャーは複数のフォワーダーに同時に送信することが可能で、サブスクライバーは複数のサブスクライビング フォワーダーに接続してメッセージを受信できます。したがって、パブリッシャー、サブスクライバー、フォワーダーの間の接続を介したメッセージのフローは複雑になる可能性があります。次の図は、トピックとサブスクリプションごとにメールが流れる仕組みを示しています。色の違いでメッセージがパブリッシャーからサブスクライバーに流れるパスの違いを表わしています。

複数のパブリッシャーから配信されたメッセージは、パブリッシュとサブスクリプションのフォワーダーを経由してサブスクライバーに送信されます。

Pub/Sub を継続稼働させる

Pub/Sub のような分散システムが継続的に稼働し、すべてのお客様に対して効果的にサービスを提供するには、システムの包括的な可視化と制御が必要となります。サービスのメンテナンスは、Google のサイト信頼性エンジニア(SRE)が責任を持ちます。Pub / Sub の場合、エンジニアの 24 時間年中無休のサポートを提供するため、世界中の複数の場所に拠点を置いています。

環境

Pub/Sub のようなシステムのメンテナンスでは、まず、お客様が使用する前にソフトウェアをテストできることが必要です。それを可能にするために、テスト、ステージング、本番の 3 つの Pub/Sub 環境が用意されています。テスト環境やステージング環境には、お客様のトラフィックは一切含まれません。Google が用意した継続稼働テストとモニタリングだけが含まれており、リリース時に問題を発見できるようになっています。テスト環境とステージング環境では、本番環境の前にソフトウェアの新規リリースを適用できます。テスト環境とステージング環境の違いは、ステージング環境が、ソフトウェア バージョンやコマンドライン フラグを含めて、現在またはごく近い将来の本番環境の正確なレプリカだという点です。テスト環境は、その時点でデベロッパーの作業対象となっている機能や将来リリース予定の機能などが有効になっています。

ロールアウト

Pub/Sub のロールアウトとテストの手順は、発生し得る影響を最小限に抑えるように設計されています。新バージョンの Pub/Sub をロールアウトする際の標準的な手順は次のとおりです。

  1. すべてのユニットテストと統合テストに合格したことを確認します。

  2. すべてのサーバーの新バージョンをビルドします。

  3. テスト環境とステージング環境に新サーバーをデプロイします。

  4. テスト環境とステージング環境でサーバーを数日間稼働します。

  5. 既知の問題が発生しなかった場合、サーバーをカナリアにリリースします。カナリアとは、お客様のトラフィックを少量備えた本番環境のサブセットのことを指します。

  6. カナリアで問題が検知されなかった場合、徐々に範囲を広げながら数日間かけてサーバーを本番環境にロールアウトしていき、最終的に全体にリリースします。

Pub/Sub は、コントロール プレーンとデータプレーンの分離などを通じて、障害に対する優れた復元力を備えており、新バージョンのサーバーのロールアウトはお客様にとってシームレスに行われ、パフォーマンスへの影響をお客様が感じることはありません。

モニタリング

Pub/Sub を継続稼働する鍵は、問題が生じた場合でも、エンドユーザーが認識する前に自動的に検知し、影響を抑えることにあります。このためには、システムの広範なモニタリングが必要となります。 サイト信頼性エンジニアリング チーム(SRE)は、システムの動作を説明するために明確に定義された指標である、サービスレベル インジケーター(SLI)のセットを維持しています。指標の例としては、「CreateSubscription リクエストを完了するまでにかかる時間」や「Publish リクエストによって生成されたエラーの頻度」などがあります。各指標はさまざまな方法で測定されます。一部の指標は、Google のフォワーダーやルーターの内部のみを対象としています。たとえば、「メッセージをディスクに書き込むのにかかる時間」などが該当します。

すべての指標を考慮して、内部のサービスレベル目標(SLO)が定義されます。SLO とは SLI の具体的な目標のことで、たとえば「CreateSubscription リクエストは完了するまでに 5 秒を超えてはならない」といったものです。SLO 違反があった場合、SRE に警告が通知され、SRE はその後 5 分以内に対応することが求められます。

サービスレベル契約(SLA)には SLO のリストが記述され、エンドユーザーに対して Google が保証するパフォーマンスの水準と、その水準を満たさなかった場合の措置について定義されています。Pub/Sub の SLA をご確認ください。

Google では、想定通りにパブリッシュやサブスクライブを行う一連のクライアントを整備しています。これらはプローバーと呼ばれます。プローバーには、データプレーン用とコントロール プレーン用があります。各プローバーは、お客様が行うのと同じ具体的な操作を実行し、その操作にかかった時間を測定します。たとえば、新規サブスクリプションを作成してメッセージをパブリッシュし、そのサブスクリプションの作成とメッセージの受信にかかった時間を測定するプローバーなどがあります。指標のいずれか 1 つでも想定どおりではないとプローバーが判断した場合、SRE に警告が通知されます。

サーバーとプローバーの指標の概要は、いくつかの内部ダッシュボードに表示されます。これは SRE が問題を診断する際に最初に確認する箇所です。各ページでは、サービス全体の統計データやグラフを簡単に表示できます。トピック別やデータセンター別、タスク別のデータも表示できます。

サービスのユーザーにとって最も興味深い指標は、Google Cloud Monitoring を通じてエクスポーズされます。

制御

Pub/Sub のパフォーマンスを調整するために、さまざまな制御機能が用意されています。データセンターやマシンの停止に対処するための制御機能もあります。トピックの一部またはすべてにルーティング制約を設定することもできます。ルーティング制約は、フォワーダー セットに接続できるクライアント セットや接続できないクライアント セットを指定するルールです。ルーティング制約を使用することで、想定どおりに機能していない個々のフォワーダー タスクやデータセンター全体からトラフィックを切り離すことができます。

他に Google が調整可能な機能として、フロー制御があります。この機能により、スループットを最大化しつつ、サービス内の過負荷状態を防ぐことができます。フロー制御はトラフィック シェーピングの一形態であり、予期せぬ形で負荷が突然急増した場合でも時間をかけて平坦化することが可能なため、サービスの安定性を高めることができます。フロー制御は、システム規模でも、トピック単位でも、サブスクライバー単位でも操作可能です。また、転送されるメッセージ数やバイト数を制限する方式や、未処理のメッセージ数やバイト数を制限する方式を選ぶことができます。この場合の「未処理」とは、クライアントに配信されたが確認応答が返信されていない、ということを意味します。フロー制御とルーティング制約により、お客様は細かいことを気にすることなく、Pub/Sub のパフォーマンスを最適化できます。

概要

Pub/Sub のようなサービスが提供する、スケーラビリティ、可用性、レイテンシという 3 つの利点は、マネージド クラウド サービスへの移行をお考えのお客様にとって価値のあるメリットであると言えます。非同期メッセージ サービスは、このような特長を念頭にビルドする必要があります。大量のメッセージを迅速、確実に配信した 10 年以上の経験を持つ Pub/Sub チームは、Google の最も基本的なサービスの要求に対応できるサービスを構築し、維持しています。その同じサービスが、今、メッセージを世界中に送信したいと希望するすべての外部のお客様にご利用いただけるようになりました。現行の自社メッセージ システムの 2 倍、10 倍、あるいは 100 倍の負荷にも心配する必要はもうありません。

用語集

用語 説明
クラスタ 通常、1 つの障害発生ドメイン(共有ローカル ネットワークや共有電源)を共有するマシンの論理グループ。
コントロール プレーン データプレーン上のサーバーに対するパブリッシャーとサブスクライバーの割り当てを処理する Pub/Sub のレイヤ。
データプレーン パブリッシャーとサブスクライバーの間で移動するメッセージを処理する Pub/Sub のレイヤ。
フォワーダー データプレーン内のサーバー。
グローバル データアクセス Pub/Sub パブリッシャーとサブスクライバー クライアントは、データの場所を意識しません。すべてのルーティングと保存は、ロケーション制限ポリシーに従って、サービス独自で行われます。
水平スケーリング 負荷の増大に対して、サービスのコンポーネントのインスタンス数を増やすことでシームレスに処理できるサービスの機能。
メッセージ Pub/Sub を通って移動するデータ。
ネットワーク距離 2 点間の接続に関するレイテンシの尺度。
プローバー クライアントとして機能し、Pub/Sub サーバーで 1 つ以上のアクションを予測どおりに実行するタスク。
パブリッシュ メッセージ ソース パブリッシング フォワーダーによって受信と保存が行われたメッセージ セットと、トピックに属するすべてのサブスクリプションによって確認応答が返信されたメッセージの ID のセット。
パブリッシュ / サブスクライブ(Pub/Sub)サービス メッセージの送信者とメッセージの受信者を切り離す方式のメッセージ サービス。
パブリッシャー 特定のトピックに関するメッセージを作成して送信(パブリッシュ)する Pub/Sub のクライアント。
ルーター コントロール プレーン内のサーバー。
ルーティング制約 可能性のある接続先のエンドポイントとして、ルーターによってクライアントに送信されるフォワーダーと送信されないフォワーダーを示すルールのリスト。
サービスレベル契約(SLA) お客様に対して Google が保証するパフォーマンスの水準と、その水準を満たさなかった場合の措置について定義する SLO のリスト。
サービスレベル インジケーター(SLI) システムの動作を説明するために明確に定義された指標。
サービスレベル目標(SLO) サービスレベル インジケーターの具体的な目標。
サブスクライバー 指定したサブスクリプションに関するメッセージを受信する Pub/Sub のクライアント。
サブスクリプション 特定のトピックに関するすべてのメッセージの受信に関心があることを示す名前付きエンティティ。
同期ポイント保証 サブスクリプションが作成されると、その時点以降にパブリッシュされたすべてのメッセージがそのサブスクライバーに配信されること。
トピック メッセージのフィードを示す名前付きエンティティ。