Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

Redis Pub/Sub入門ガイド:パブリッシュ/サブスクライブパターンの基礎と特徴

Publish/Subscribe(通称Pub/Sub)は、ソフトウェアエンジニアリングの世界で数十年にわたって使われてきたメッセージングパターンです。しかし従来は専用のメッセージングサーバーや専門知識が必要だったため、気軽に導入できるものではありませんでした。

RedisのPub/Subは、このパブリッシュ/サブスクライブパターンを軽量かつシンプルに実装したものであり、Redis 2.0以降のすべてのRedisサーバーに標準搭載されています。そのため、Redisが稼働している環境であればどこでも利用可能で、高速でシンプルなPub/Subシステムを短時間で構築できます。

自社のアプリケーションにRedis Pub/Subが適しているかを見極めるには、まずPub/Subというパターン自体の設計思想と目的を理解し、そのうえでRedis実装ならではの特徴を検討することが重要です。

Pub/Subの概要

「パブリッシャー」と「サブスクライバー」を分離する設計

依存関係を減らし、知識をコンポーネントごとに分離することは、スケーラブルなソフトウェアを設計するうえで重要な目標です。パブリッシュ/サブスクライブ(Pub/Sub)パターンは、メッセージを発行する部分と、メッセージを受け取って処理する部分を明確に切り離します。イメージをつかむために、具体的な例を見てみましょう。

「パブリッシャー」の例

  • チャットルームにメッセージを送信するアプリ
  • 自身の稼働状態や提供サービスを通知するDockerコンテナ
  • 株式取引の約定価格を告知するアプリ
  • 家庭内の温度センサーが計測値を通知する仕組み
  • マルチプレイヤーゲームでのメッセージ配信(「Elonはgrueに食べられてしまった!」)

「サブスクライバー」の例

  • チャットルームの新着メッセージを待ち受けるアプリ群
  • 通知をSlackへ転送するアプリ
  • リアルタイムの状況を確認するモバイルクライアント
  • イベントを記録し、後から分析できるようにするログサービス

Pub/Subパターンの大きな利点は、パブリッシャーを開発するときにサブスクライバーのことを深く意識せずに済み、その逆もまた然り、という点です。上記の例では、1つのパブリッシャーからのメッセージに対して複数のサブスクライバーが関心を持つこともありますし、すべてのパブリッシャーを購読したいサブスクライバーが存在する場合もあります。

Pub/Subにおいて、パブリッシャーは自分のサブスクライバーについて知る必要がありません。パブリッシャーはメッセージをチャネル(他のシステムではトピックと呼ばれることもあります)に送り出すだけで、そのまま次の処理へ進めます。メッセージが発行された時点でそのチャネルを購読していたサブスクライバーだけが、そのメッセージを受け取ります。

一方、サブスクライバー側は1つ以上のチャネルを購読し、届いたメッセージに反応するよう設計します。ここで重要なのは、サブスクライバーがメッセージの流量に追いつけない場合、取りこぼしが発生するという点です。これは一見欠点のように思えますが、実際には有効な設計判断です。低速なサブスクライバーの能力に引きずられることなく、システム全体をスケールさせられるからです。パブリッシャーはサブスクライバーの状態に影響されることなく、迅速に処理を先へ進められます。

Pub/Subの目的:「作業負荷」ではなく「配信」をスケールさせる

Pub/Subはスケーリングのためのパターンですが、どのような種類のスケーリングに役立つのかを正しく理解しておく必要があります。ここで重要になるのが、Pub/Subとメッセージキューイングの違いです。

キューイングパターンでは、キュー(Redisではリスト)が処理待ちのメッセージをバッファリングし、ワーカーのプールがそこからアイテムを取り出して処理します。このモデルでは、各メッセージは必ず1つのワーカーだけに渡されるため、ワーカーの数を増やせばキューの処理速度も比例して向上します。どのワーカーも、同じメッセージをまったく同じ方法で処理します。

対照的にPub/Subでは、チャネル上のすべてのメッセージを、そのチャネルの全サブスクライバーへ配信しようとします。これは多対多のパターンであり、それぞれのサブスクライバーは受け取ったメッセージに対して固有の処理を行います。あるサブスクライバーは永続ログへの書き込みを行い、別のサブスクライバーはSlackチャネルへの投稿を行い、さらに別のものは営業オフィスのベルを鳴らす、といった具合です。

要するに、Pub/Subはメッセージの配信をスケールさせ、キューイングはメッセージの作業負荷処理をスケールさせるパターンだと言えます。Redisはどちらの用途にもよく使われており、キューイングの有名な例としてはSidekiqが挙げられます。

Redis Pub/Subの特徴的な挙動

Pub/Subは古くから存在するパターンですが、中核となる考え方は共通していても、実装ごとの具体的な機能にはかなりの違いがあります。

RedisのPub/Subは軽量で高速な実装です。その設計思想を理解するには、あえて「備えていない」機能に注目すると分かりやすいでしょう。

  • 永続化や値のキャッシュなし
  • 配信保証なし
  • クラスタ最適化は未実装(現時点)

永続化なし:「チャットルームみたいなものでは?」という疑問について

ディスクへの書き込みが可能な他の多くのRedis操作とは異なり、Redis Pub/Subは非永続的です。発行されたメッセージは直接サブスクライバーへ渡された後、破棄されます。Redisのメモリにもディスクにも記録は残りません。

この挙動は、初心者をときどき混乱させます。「Redis Pub/Subはチャットルームの実装によく使われる」と聞いていたのに、と。私たちがSlackのようなツールを思い浮かべるとき、それは「ログインすると過去のメッセージが見えて、その後は新しいメッセージが全部届く」というものです。しかし実際には、「過去のメッセージを見る」機能はPub/Subの範囲外であり、別の手段で実現する必要があります。Pub/Subが担うのは新しいメッセージの配信だけです。

この意味で、Pub/Subはライブ配信に似ています。視聴を開始すればその瞬間から情報は流れてきますが、開始前に何が起きていたかは分かりません。余談ですが、インターネットリレーチャット(IRC)も組み込みの履歴やメッセージ保持を持たないPub/Subモデルを採用しています。だからこそ「チャットルーム」という比喩がこれほど一般的なのです。

とはいえ、RedisとPub/Subを組み合わせれば、もちろんチャットルームを実装できます。その場合は、メッセージを発行すると同時にリストにもプッシュしておき、ユーザーがメッセージ履歴を参照できるようにすればよいのです。

配信保証がない

Pub/Subモデルでは、サブスクライバーがメッセージを確実に受け取れる保証はありません。ネットワークに問題が発生したり、メッセージを読み取る速度が追いつかなかったり、メッセージ発行時に接続が切れているように見えた場合、そのサブスクライバーは単純にメッセージを受け取れません。また、誰も購読していないチャネルに向けてメッセージを発行することも可能ですが、そのメッセージはそのまま失われます。

一部の他のメッセージングシステムでは、既読確認(ack)やサブスクライバーごとのバッファリングによって、短時間の切断をカバーする仕組みを備えています。Redisはここでシンプルな選択をしました。逃したメッセージは逃したままである、と。必ず相手に届ける必要があるメッセージは、別の手段で配信しなければなりません。

このトレードオフは言葉にすると不利に聞こえますが、実際にはそれほど深刻ではありません。メッセージ確認応答やサブスクライバー個別のバッファリングを省いたことで、Redis Pub/Subは非常に高速にメッセージを処理できます。そして「繋がっている間だけ確実に届けばよい」という性質のメッセージ配信で恩恵を受けるシステムは、実はたくさん存在するのです。

クラスタスケーリングはまだ効率的ではない…現時点では

Pub/Subはそもそもスケーリングのためのソリューションです。だからこそ、メッセージだけでなく、その配信を担うサービス自体のスケーラビリティも考慮する必要があります。RabbitMQやKafkaをはじめとする多くのメッセージングシステムは、高可用性と合理的なスケーリング特性を前提に設計されています。

Redis Cluster(Redis 3.0から標準搭載)はRedis Pub/Subをサポートしていますが、重要な注意点があります。発行されたすべてのメッセージはクラスタ内の全ノードにブロードキャストされるため、大規模クラスタではトラフィックが急増し、すぐに圧迫されてしまう可能性があるのです。

現時点での結論はシンプルです。Pub/Subは単一のRedisサーバー上で使うか、Pub/Sub専用の小規模クラスタに限定しましょう。どちらの構成でも大量のメッセージを十分に捌けます。長期的には、必要なノードにのみメッセージをルーティングするスマートな機能がRedis Clusterに追加される予定ですが、この機能はまだ設計段階にとどまっています。

まとめ

全体として、Redis Pub/Subはソフトウェアをスケールさせるための有用なツールです。本記事で見てきた「備えていない機能」の数々は、いずれも設計上のトレードオフの結果です。あらゆるメッセージング用途を万能にこなす代わりに、シンプルさと速度を選んだのです。

Redisの他の多くの機能と同様に、Pub/Subはすべての特殊なユースケースに最適化されているわけではありません。しかし、その特性をきちんと理解していれば、かなり長い道のりを支えてくれる存在になります。Redisの他の機能と組み合わせれば、開発者の道具箱の中でも非常に強力なツールとなるでしょう。

ここまで概観をつかんだところで、次は実際のコード例を見たくなったはずです。続編の「Redis Pub/Subの実装方法(How To)」もぜひチェックしてみてください。

  1. Redis SUBSCRIBEコマンド徹底解説 – Pub/Subで複数チャネルを同時に購読する方法

    本チュートリアルでは、redis-cliを使ってRedisのメッセージブローカー(Pub/Sub)システム上で、複数のチャネルを同時にサブスクライブ(購読)する方法を詳しく解説します。 SUBSCRIBEコマンドとは SUBSCRIBEコマンドは、クライアントをRedisメッセージブローカーシステム内の1つ以上の指定チャネルに登録するためのコマンドです。クライアントがSUBSCRIBEコマンドを実行すると「サブスクライブ状態」に入り、登録済みのチャネルを待ち受けます。他のクライアントが該当チャネルに対してPUBLISHコマンドでメッセージを発行すると、Redisはそのメッセージをすべての購読

  2. JedisライブラリでRedisのPub/Subシステムを実装する方法【Javaサンプルコード付き】

    このチュートリアルでは、Java向けクライアントライブラリ「Jedis」を使用して、RedisのPub/Sub(パブリッシュ/サブスクライブ)システムを実装する方法を解説します。 Jedisライブラリとは Jedisは、Redisデータストア用のJavaクライアントライブラリです。軽量で非常に扱いやすく、Redis 2.8.x、3.x.x以降のバージョンと完全な互換性があります。シンプルなAPI设计で、Redisの各種コマンドを直感的に操作できるのが特徴です。 RedisのPub/Subシステムとは Redisは、Publish/Subscribe(パブリッシュ/サブスクライブ)型のメッセージ