RabbitMQについて
今回フォーカスするRabbitMQは、AMQPという柔軟性と信頼性に富んだメッセージ転送を実現するプロトコルの実装になります。AMQPは、2003年にJPMorgan Chaseで開発されたメッセージ転送プロトコルで、柔軟なメッセージルーティングの実現に加え、送信元から送られたメッセージのキューへの格納、およびキューから宛先へのメッセージの送信処理をトランザクション処理させること、またメッセージの永続化機能によって信頼性の高いメッセージ転送処理を実現できます。
何より、金融系で10年近くメッセージ転送ミドルウェアとしての実績があるプロトコルということで、エンタープライズシステムを含む多くのシステムで利用されてきています。
RabbitMQはそんな信頼と実績のAMQPの実装として、有名どころとしてはOpenStackやSensu、その他Mozilla PulseやGoogle rocksteadyなど、分散コンピューティングを実現する多くのOSSから支持されてきました。またOSSだけでなくVMware NSXなどのエンタープライズ製品などでも広く利用されています。
さらにRabbitMQが提供するプラグイン機構によって、MQTTやSTOMPなど、さまざまなMQプロトコルをサポートしています(これらについては、また別の機会に紹介したいと思います)。
Kafkaについて
信頼と実績のAMQPはその後、Goldman SachsやJPMorgan Chaseなどの金融系、MicrosoftやCiscoなどのエンタープライズ系の頑張りによって、2014年にAMQP 1.0がISOで標準化されました。
これに対して「AMQPだから良いってわけじゃねーから("What are the differences between Apache Kafka and RabbitMQ?"より)」と異を唱えるWeb屋さん「LinkedIn」がリアルタイム性にこだわった独自のMQシステムを開発し、その後OSSとして「Apache Kafka」を公開します。
Apache Kafkaでは、大量のイベントストリームに対して、高いスループットが出せるようにし、加えて定常的にデーモンがメッセージを取得し続けるシステムだけでなく、定期的に実行されるバッチシステムによってメッセージが消費されるケース(Kafkaでは「オフラインシステム(Offline System)」と呼称)などにおいて大容量のデータがキューに滞在できるように設計されています。
パフォーマンスについては、Kafkaが毎秒で数十万メッセージのスループットが得られていることが知られています。また、Kafkaではファイルシステムを介してメッセージのやりとりを行います。後述するRabbitMQやRedisではメモリを介してデータのやりとりを行いますが、Kafkaでは取得したメッセージをファイルに書き出し、コンシューマ(Kafkaが預かっているメッセージを取得するアプリケーション)からのメッセージ取得要求に対してファイルからデータを取り出します。一般的に、メモリアクセスよりもストレージアクセスの方がスループットが低いことが知られていますが、Kafkaではシーケンシャルなストレージアクセスがランダムなメモリアクセスよりも早いという性質を生かして高いスループットを出しています。
また、KafkaはRabbitMQと比べて、実績が浅いですが、YahooやTwitterのリアルタイム解析システムにおいて利用されており、また国内のエンタープライズの領域でもKafkaが使われている事例が存在します。
RabbitMQはどうなの?
ここまでの話だと「なんだ、時代はApache Kafkaじゃん」と思われるかもしれませんが、パフォーマンスと信頼性はおおむねトレードオフの関係にあり、冒頭で述べたようにRabbitMQではAMQPで規定されているシステム側でトランザクション処理をサポートするなど、パフォーマンスを犠牲にする一方で、システム側で高信頼なメッセージ転送機能を抽象化してくれています。
どちらか一方が良いというよりかは、どちらにも良い面と弱い面があり、MQミドルウェアの選定ではシステムの要件と妥協できるポイントを考えて選択するのが良いと思います。
なお、RabbitMQのパフォーマンスに関しては、メッセージサイズにもよりますが、おおむねノードあたり数万メッセージ/秒程度であればさばける模様です。
Lazy Queuesについて
さて。ここでようやく本題ですが、冒頭で簡単に説明したようにRabbitMQでは、受け取ったメッセージをインメモリのデータベースに保存します。もちろん、メモリだけに保存されている場合には当該ノードで障害が発生した際にメッセージが失われてしまうため、キューに対してメッセージを永続化させるように設定できます。また、メモリ容量が逼迫(ひっぱく)してきた際にはRabbitMQはメッセージをストレージに書き出す処理(ページアウト処理)を実行します。ページアウト処理中はキューをブロックしてしまうため、その間RabbitMQはメッセージを受け取ることができません。このためKafkaでできるようなオフラインシステムからの利用など、大容量のデータがキューに滞在するようなユースケースでの利用では大きな制限がありました。
この問題を解決する仕組みとして2015年12月にリリースされたRabbitMQ v3.6.0に「Lazy Queues」という機能が追加されました。Lazy Queuesが設定されたキューでは、Kafkaと同じようにキューに到着したメッセージはすべてファイルに書き出されます。これにより、キューによって消費されるメモリ容量が劇的に減り、大容量のデータがキューに滞在するようなユースケースにも対応できるようになりました。