G-gen の杉村です。Google Cloud (旧称 GCP) で Pub/Sub を中心とした疎結合アーキテクチャについて解説します。
はじめに
疎結合アーキテクチャとは
疎結合アーキテクチャとは、当記事では以下のものを指すこととします。
「システム間のメッセージ連携にメッセージングサービスを用いてシステムを疎結合化すること」
Google Cloud では、ズバリ以下のようなアーキテクチャを意味します。
図の左側の「画像アップロード受付 API」は、システム利用者からの画像のアップロードを受け付けて「画像ストレージ (変換前)」に画像ファイルのローデータを保存します。その後、画像の変換処理を依頼するメッセージを図中央のメッセージキューに投入します。図の右側の「画像変換処理」はメッセージキューから仕事の依頼を見つけると、画像に所定の変換処理をかけてから「画像ストレージ (変換後)」に保存します。
画像のアップロード受け付けと、画像の変換処理という異なる仕事を、別々のシステム (サービス) が分担しているイメージです。
比較のために、最大シェアを誇るパブリッククラウドである Amazon Web Services (AWS) でも同様のアーキテクチャを構成図にしてみました。
このような疎結合なアーキテクチャは「サーバーレスアーキテクチャ」や「マイクロサービスアーキテクチャ」が一般化するにつれ、理解しているのが当然ともされるようになってきています。当記事では、このようなアーキテクチャについて論じていきます。この記事が、みなさまの理解の助けとなれば幸いです。
非同期処理
同期と非同期
疎結合アーキテクチャを理解するうえで重要なのが 同期処理 と 非同期処理 の違いについてです。
なお、ここでは特にプログラミングの背景で語られる場合の同期処理と非同期処理についてではなく、Web API へのリクエスト・レスポンスの背景で説明します (ただし、本質的にはこれらは同じです)。
同期処理
同期処理 とは、クライアントが API に対して処理を依頼するリクエストを行ったあと、期待するレスポンスが返ってくるまで待機してから次の処理に進む方式を指します。
例えば gcloud コマンドで、ある Cloud Storage バケットの中にあるオブジェクト一覧を表示させるとします。
コマンドを実行すると、すぐにオブジェクトの一覧 (期待する処理結果) を得ることができ、その後にコマンドラインが操作できるようになります。これは同期処理です。例えば Google Cloud の参照系の API リクエストを想像すると、ほとんどの参照系 API が同期処理であると言えます。
非同期処理
一方で、仕事の依頼のリクエストをしてもすぐ期待する処理結果が返ってくるわけではなく、処理の受付だけが行われ、結果は後から取りにいくような方式が、非同期処理 です。
例えば、ある画像処理システムがあるとします。画像ファイルを指定して処理開始を命令すると「処理を受け付けました」というメッセージとその処理を一意に示す ID だけが返ってきます。画像の処理には数分かかるので、処理が完了したかどうか、ID を示してたびたび問い合わせる必要があります。このような処理が非同期処理です。
処理開始を指示する最初のリクエストを ジョブの投入 と表現する場合もあります。
疎結合アーキテクチャと非同期処理
メッセージングサービスを用いた疎結合アーキテクチャは、この非同期処理を実現するためのものと言えます。
仕事を依頼する側 (ジョブを投入する側) と、仕事を行う側 (ジョブを処理する側) に分かれて、その間にメッセージングサービスが仲介役として入るような構成です。
仕事を依頼する側は、とりあえず仕事をメッセージキューに投入します。メッセージキューに入った仕事を処理するのは、処理者の仕事です。
メリット
メッセージングサービスが必要な理由
上記のようなアーキテクチャでは、左側のシステム (システム A とします) と右側のシステム (システム B とします) の間に Cloud Pub/Sub や Amazon SQS といったメッセージングサービス、すなわちシステム間のメッセージを中継するためのサービスが入っています。
このような構成図を初めて見る方にとっては、以下のような疑問が生まれるでしょうか。
- なぜわざわざ間に Pub/Sub を挟むのだろうか。システム A から B へ直接メッセージを送れば早いのではないか。
- あるいはシステム A と B を一つのシステムにしてしまえば早いのではないか。
しかしながら、メッセージングサービスをシステム間に挟み込むことには、明確なメリットがあります。それは、以下で表すことができます。
- 拡張性の向上
- 保守性の向上
- 可用性の向上
拡張性の向上
メリットの一つは 拡張性の向上 です。
間にメッセージングサービスを挟むことで、仕事を依頼する側であるシステム A と、仕事をこなす側であるシステム B を別々のシステムとして分離することができています。そのため「最近、1件あたりの仕事が重くなってきたから、システム B だけ処理能力を向上させたい。システム B だけサーバ (コンテナ) の数を増やそう」というように、システム拡張の柔軟性が増します。
仮にシステム A と B が単一のサーバ上で稼働する単一のシステムであれば、このような柔軟な処理能力拡張は不可能です。
保守性の向上
もう一つのメリットは 保守性の向上 です。
仕事を依頼するシステム A と、仕事をこなすシステム B を別のシステムとして分離することで、プログラムの修正があった場合の影響範囲を狭めることができます。
ビジネスの変化のスピードが早いこんにちでは、システムの保守性を高め、デプロイの頻度を上げることが望まれます。保守性の向上は、ビジネスメリットをもたらすと言えます。
またこれは モジュール独立性の向上 と言い換えることもできます。モジュール独立性という用語は基本情報技術者試験に向けた学習で目にしたことがある方も多いかもしれません。仕事を依頼する側とされる側でモジュールを分けることで モジュール強度が向上 し、仕事の内容を「メッセージ」としてやり取りし合うことで モジュール結合度を下げる ことができます。これが、保守性の向上に繋がっています。
可用性の向上
無視できないメリットとして 可用性の向上 も挙げられます。
システム A と B が単一システムであれば、システム障害が起こった際には両方の機能が停止します。
しかしこれらが別々のシステムであれば、例えばシステム A が停止しても、システム B は既に依頼された分の仕事は継続してこなせますし、逆の場合なら、仕事の受け付けだけは継続できます。
なぜクラウドらしいのか
なぜ疎結合アーキテクチャは AWS や Google Cloud といったプラットフォームによく見られ「クラウドライクなアーキテクチャである」と認識されているのでしょうか。
このようなアーキテクチャ自体はオンプレミスでも実現可能であり、事実 IBM MQ のようなメッセージングのためのソフトウェアは何十年も前から存在します。
このアーキテクチャがクラウドで最も用いられる理由は、前述の 拡張性メリットがクラウドでより効果的に得やすいから です。
パブリッククラウドでは、仮想サーバやコンテナといったコンピューティングリソースを API コールでプログラマブルに、また即時に増強することができます。前述の例で言えば、システム B だけを増やしたいと思えばボタン一つで、あるいはボタンを押すことすらなく負荷に応じて自動的にリソースを増強することができるのです。
つまり、疎結合アーキテクチャ自体はクラウドに特有のアーキテクチャというわけではなく、クラウドで特にメリットが得やすいアーキテクチャなのです。
用語
コンポーネントの用語
メッセージングサービスを使った疎結合アーキテクチャでは、登場するコンポーネント (登場人物、と言い換えてもいいでしょう) の役割を「仕事を依頼する主体」「仕事を仲介する主体」「仕事を処理する主体」と大きく分けることができます。
多くの場合、仕事を依頼する主体を producer と呼びます。ジョブやメッセージを生成 (produce) する側だからです。
仕事を仲介する主体は queue (Message Queue) と呼ばれます。
仕事を処理する主体は consumer と呼ばれます。ジョブやメッセージを消費 (consume) して処理するからです。
挙動の用語
pull と push
Producer が生成したメッセージが consumer に届くには複数の方法があります。
consumer から queue にポーリングを行いメッセージを取りに行く pull 方式と、メッセージが consumer に直接届けられる push 方式があります。処理やプログラムの性質に応じて、適したほうが選択されます。
FIFO (message ordering)
また queue の性質として FIFO という用語があります。First-In First-Out の略で「先入れ先出し方式」とも表現されます。
一般的なメッセージングサービスでは、メッセージは FIFO ではなく、たびたび順番が入れ替わって出てきます。サービスによってはオプションで FIFO を保証する設定が可能で、有効化することでメッセージが順番通りに出てくるようになります。ただし一般的には、FIFO を有効化するとメッセージのスループット (一定時間に配信可能な数量) は低下します。
また FIFO の代わりに message ordering のような別の用語が用いられる場合があります。
At-least-once (最低1回の配信)
一般的なメッセージングサービスでは、メッセージングの配信は At-least-once (最低1回の配信) という性質で行われます。
この言葉は、メッセージが最低1回は配信されるが、逆に言うと2回以上配信される場合もあるという性質を指します。メッセージングサービスが分散システムであること、また一時的なネットワーク障害や遅延でメッセージに対する ack (受信したことを伝えるレスポンス) がうまく伝達されない場合もあることなどに起因します。
同じメッセージが2回以上配信されてもシステム全体で不具合が起きないよう、consumer が何回同じ処理をしても、その結果としての状態が同じになるように、処理の設計を工夫する必要があります。このように、処理を複数回行っても結果が同じになる性質のことを 冪等性 (べきとうせい) と呼びます。分散システムと非同期処理が普通である現代 IT では、重要な概念とみなされています。
サービスによっては Exactly-once (1回限りの配信) というオプションが用意されている場合があります。FIFO の場合と同じく、有効化すると通常はスループットが低下します。
アーキテクチャの用語
広義の Pub/Sub
producer, queue, consumer という用語を紹介しましたが、よく似たアーキテクチャの場合でも、違う用語が用いられることがあります。
Publish/Subscribe 方式 というアーキテクチャがあります。Pub/Sub 方式と略される場合もあり、これは Google Cloud のいちサービスである Cloud Pub/Sub とは異なり広義の Pub/Sub と呼べるでしょう。
Pub/Sub 方式とは、一つのメッセージキューに対して複数の subscriber がいるような構成を指します。
このとき producer は publisher (発行者)、queue は topic (トピック)、consumer は subscriber (購読者) と呼ばれます。
Fan-out (ファンアウト)
冒頭で紹介したような画像処理のシステムでは、画像変換のジョブが投入されたあと、subscriber (consumer) のうち誰か一人が処理をすれば用が足りるような仕組みでした。
しかしそうではない場合、例えば「subscriber 1 はサムネイル画像を作る」「subscriber 2 は画像フォーマットを変換する」「subscriber 3 は AI/ML 推論を行い画像の内容を説明するメタデータを付与する」のように別々の目的の subscriber に同時にメッセージを送りたい場合もあります。
このように複数の subscriber に同時にメッセージを送るようなアーキテクチャを Fan-out (ファンアウト) と言います。ここでの Fan は「扇」であり、扇の形のようにメッセージがばらまかれることから来ています。
なお、Google Cloud の Cloud Pub/Sub では上記の Fan-out アーキテクチャを一つの topic に対して複数の「subscription リソース (topic の "出口" の役割)」を作成することで実現し、AWS では Amazon SNS と Amazon SQS の組み合わせで実現します。
- 参考 : Google Cloud - 1 対多の Pub/Sub システムの構築
- 参考 : AWS - 一般的な Amazon SNS シナリオ
Google Cloud での疎結合アーキテクチャ
キーとなるサービス「Pub/Sub」
Google Cloud では疎結合アーキテクチャのキーとなるサービスとして Cloud Pub/Sub が挙げられます。
Pub/Sub はフルマネージドなメッセージングサービスであり、インフラの管理が全く必要ありません。高いスケーラビリティを持ち、Publisher/Subscriber 形式で非同期メッセージングを行うことができます。
AWS との比較
AWS を既にご存知の方に分かりやすいよう表現すると、Pub/Sub は「Amazon SNS と Amazon SQS と Amazon Kinesis Data Streams を合体させたようなサービス」と言えます。
Pub/Sub では Amazon SNS のような Push 型 / ファンアウト型のメッセージングを行うことができるのと同時に、Amazon SQS と同じ Pull 型 (ポーリング型) のメッセージングを扱うこともできます。
また Pub/Sub は Amazon Kinesis Data Streams のようなストリーミングバッファの側面もあります。Pub/Sub は大量のスループットを処理でき、またメッセージの data フィールドの最大サイズも 10 MB と大きいものになっています。
また Google Cloud の各種サービスから発動したイベントの配信に Eventarc のバックエンドとして用いられる点は、Amazon EventBridge と役割が重複するところもあります。
まとめると Pub/Sub は、AWS であれば Amazon SNS、Amazon SQS、Amazon Kinesis Data Streams、Amazon EventBridge 等から適切なサービスを選択するべきな以下のようなユースケースに対応できます。
ユースケース | AWS サービス | Google Cloud サービス |
---|---|---|
ジョブの非同期・並列処理 | Amazon SQS | Cloud Pub/Sub |
ユーザー操作やサーバイベントの取り込み | Amazon Kinesis Data Streams | Cloud Pub/Sub |
IoT からのデータストリーミング | Amazon Kinesis Data Streams | Cloud Pub/Sub |
イベントドリブン処理の実行 (イベントバス) | Amazon EventBridge | Cloud Pub/Sub |
※表のサービス名は一例であり実際には他のサービスも使用されます
Cloud Pub/Sub vs Cloud Tasks
Google Cloud には Cloud Pub/Sub とは別に、メッセージングと非同期処理を実現するプロダクトとして Cloud Tasks があります。
Cloud Pub/Sub と Cloud Tasks はどちらも内部にキューを持っており publisher が依頼する処理を非同期にするという点でよく似たサービスではありますが、想定アーキテクチャが異なっています。
Cloud Pub/Sub では publisher と subscriber を分離してアーキテクチャを疎結合にすることが前提とされていることに対し、Cloud Tasks は subscriber を明示的に呼び出し、subscriber による処理を制御できることがポイントとなっています。配信タイミング・レートを管理することも可能です。詳細は以下のドキュメントをご参照ください。
サンプルアーキテクチャ
データの並列処理
当記事の冒頭でも引き合いに出したアーキテクチャです。
ユーザから画像のアップロードを受け付けた左側のシステムは、画像を Cloud Storage にアップロードしたあと、Pub/Sub にメッセージを投入します。メッセージにはバケット名や画像ファイルのパスが入っています。右側のシステムは Pub/Sub (の subscription) をポーリングし、pod (subscriber) のうち誰か一人がメッセージを取得すると、対象の画像を処理します。
大量の Pod (subscriber) で次々に投入されたジョブを処理するので、高いスループットを維持することができます。
publisher (左側) と subscriber (右側) はそれぞれ Google Kubernetes Engine の pod であり、高いスケーラビリティを持っています。
サーバーレス
高いスケーラビリティは、サーバーレス・アーキテクチャで特に発揮されます。サーバーレスと疎結合アーキテクチャは、切っても切れない関係性があります。
サーバーレス・アーキテクチャの基本については、以下の記事もご参照ください。
他のクラウドサービスとの連携
Pub/Sub のようなメッセージングサービスは、クラウドサービス間の連携にもよく用いられます。
上記の図は、以下のような処理を示しています。項番は図中の数字と対応しています。
- 動画ファイルが Cloud Storage に設置されると、Cloud Functions が起動する (イベントドリブン)
- Cloud Functions は Transcoder API (フルマネージドの動画変換サービス) へ非同期ジョブを投入する
- 元動画ファイルのパスや変換後動画ファイルの出力先 Cloud Storage パスを指定
- Transcoder API は与えられたパラメータに基づき Cloud Storage から元動画ファイルを取得し、変換処理を実行
- 動画変換処理が終わると、Transcoder API は変換後動画ファイルを別の Cloud Storage に配置
- Transcoder API はジョブ完了を Pub/Sub に通知
- Pub/Sub をサブスクライブしていた別の Cloud Functions が、後続処理を実行
杉村 勇馬 (記事一覧)
執行役員 CTO / クラウドソリューション部 部長
元警察官という経歴を持つ現 IT エンジニア。クラウド管理・運用やネットワークに知見。AWS 12資格、Google Cloud認定資格11資格。X (旧 Twitter) では Google Cloud や AWS のアップデート情報をつぶやいています。
Follow @y_sugi_it