gRPC, MQ, NewtMQ, RabbitMQ, Ruby, STOMP, ZeroMQ, ベンチマーク

 こんにちは、インフラ統括本部の大山 裕泰です。
 NewtMQ v0.2 をリリースしました。

https://github.com/newtmq/newtmq-server/releases/tag/v0.2.0

 v0.2 では temp-queue を実装し Brokered MQ を利用した高速な RPC 処理が実現できるようになりました。
 今回は RabbitMQ(AMQP) 及び RabbitMQ(STOMP) に加えて Broker-less MQ の ZeroMQ、Google 謹製の RPC フレームワーク gRPC、そして NewtMQ で RPC のベンチマークを取ってみました。
 尚、御馴染みの Apache Kafka には 同様の機能がない模様 なため、検証しておりません。

temp-queue とは?

 NewtMQ (v0.2) に実装した temp-queue の仕組みは、以前のツチノコブログエントリ で紹介した rabbitmq-stomp プラグインのものと同じ仕組みになります。RabbitMQ 本体にも 同様の機能 が実装されています。
 簡単に説明すると pub-sub モデルにおいては Publisher と Subscriber はそれぞれお互いの存在を認知しませんが、Subscriber が、受け取ったメッセージを送ったある Publisher に対してメッセージを返信したいケースにおいて、当該 Publisher 専用の一時キューに対してメッセージを送ることで Subscriber と特定 Publisher 間でメッセージパッシングを行えるようにする仕組みになります。

 MQ を介して RPC を行う際、この仕組みが有効に作用します。

RPC over MQ

 RPC はネットワークを介した計算機資源を活用する分散システムの世界で 30 年以上の歴史がある枯れた技術です。
 分散システムにおいてはネットワークや計算機に障害が発生した際においてもサービスが安定して稼働できること、そして計算機資源の動的な増減に対応できることが古くからの課題として知られています。これらの問題を解決する手段の一つとして MessageQueue が広く利用されてきました。
 大規模化・複雑化した今日のシステムの多くはこうした仕組みに支えられています。数千 ~ 数万のサーバから構成される OpenStack では、RabbitMQ を用いた RPC を行うアーキテクチャ を採っています。

Broker-less MQ

 ここで、今回の比較する ZeroMQ に代表される Broker-less MQ と呼ばれる MQ について紹介します。
 ’MQ’ という名前を冠している点で RabbitMQ や NewtMQ と同じく、Queuing や Pub/Sub といったシステムモデルの実装をサポートする機能を提供している点で共通ですが、メッセージを仲介する ‘Broker’ を持たない点で異なります。
 RabbitMQ や NewtMQ の場合、以下のように Pub/Sub モデルにおいてクライアント (Publisher 及び Subscriber) は Broker に対してメッセージの送信/取得を行います。Apache Kafka や MQTT の実装として知られる ActiveMQ Apollo もこちらに分類されます。
   

 対して ZeroMQ の場合、以下のように Publisher が直接 Subscriber プロセスに対してメッセージを送信します。

 Broker を持た無い MQ システムでは、Broker を介した MQ と比較していくつかのメリットがあります。まずクライアントと Broker 間の通信が無くなるため、通信に伴うトラフィック量を減らせる事ができます。また ‘Broker’ で行う処理を省くため高速なメッセージパッシングを行うことができます。

ZeroMQ

 ZeroMQ の用途は Queuing や Pub/Sub モデルの実装に止まりません。ここで ZeroMQ 自体について補足で紹介します。
 ZeroMQ では ‘ZeroMQ Socket’ と呼ばれる TCP Scoket を抽象化したオブジェクトに対して、1対1, 1対N, N対1, N対N の通信を実現する機能 を加えています。更に、これらの通信のベースに Queuing の仕組みを設ける事で通信の可用性を高め、従来の TCP Socket を用いた通信を行うアプリケーションに対して、高い機能性と可用性を備えた仕組みを提供しています。
 更に ZeroMQ の Router Socket を利用することで、メッセージの配送を仲介する Proxy をつくることができ Broker MQ のような振る舞いをさせることも出来ます。

測ってみた

 それではここで MQ を利用した RPC 処理のベンチマークを取ってみます。ベンチーマークは Broker MQ の雄 RabbitMQ(AMQP, STOMP) Broker-less MQ の代表格 ZeroMQ、そして RPC フレームワークとして現在非常に活発に開発が行われている Google 謹製の RPC フレームワーク gRPC、そして NewtMQ に対して行います。ベンチマークでは以下の RPC ベンチマークツールを利用します。

* https://github.com/userlocalhost2000/rpc-bench

 各クライアントライブラリはそれぞれ以下を使用しています。

ターゲット ライブラリ (バージョン)
RabbitMQ(AMQP) bunny (2.3.1)
RabbitMQ(STOMP) stomp (1.4.0)
NewtMQ stomp (1.4.0)
gRPC grpc (0.14.1)
grpc-tools (0.14.1)
ZeroMQ libzmq (4.2.0)
ffi-rzmq (2.0.4)
ffi-rzmq-core (1.0.5)

 今回は、各手法の実行時間の比較が目的のため、クライアント、サーバ、及び MQ を全て同一ホスト上で実行しています。実行ホスト環境は以下のとおりです。

CPU Intel Core i7-6700 CPU @ 3.40GHz
RAM 32GB
OS Ubuntu14.04
Ruby 2.2.2
RabbitMQ 3.6.1-1

 RPCBench では、クライアントがサーバに対して数値を送り、サーバが送られた数値に 1 を足して結果を返し、最終的にすべての結果が送った数値 + 1 になっているかを検査するまでに要した時間を、リクエスト数、同時実行数のパラメータを変えて取得できます。
 
 まず同時実行数を 1 に固定し、リクエスト数を 32k ~ 512k にそれぞれ設定した結果を以下に示します。

 予想通り ZeroMQ が最も早いですが NewtMQ もほぼ同じ程度の速度で RPC をさばけています。予想以上のスピードに我ながら驚いています(結果の取り違いを疑いました)。
 それぞれのベンチマークにおいて RPC サーバ処理 (数値を受け取って +1 する処理) は共通ですが、クライアントライブラリがそれぞれ異なるため、純粋に MQ の違いについての比較にはなりません。
 ただし RabbitMQ(STOMP) との比較に関しては、サーバ処理もクライアントライブラリもそれぞれ同じものを用いており、MQ だけが違うので純粋な NewtMQ と RabbitMQ(STOMP) の性能の違いがこの結果かわかります。

 続いてリクエスト数を 10k に固定し、同時実行数を 4 ~ 64 にそれぞれ設定した結果を以下に示します。

 

 やや ZeroMQ との差が開きましたが Broker MQ においては NewtMQ が最も速度が出ていることがわかります。

おわりに

 NewtMQ に temp-queue を実装したことで、RabbitMQ よりも高速に RPC を処理できることを確認しました。
 しかし NewtMQ はその他の機能面や信頼性、実績において RabbitMQ に遠く及ばないので、メジャーバージョンがリリースできるくらいまでは暖かい目で見守っていただければと思います。

 性能改善の余地はまだ残されているので、NewtMQ はまだまだ速くなると思っています。

Kafka, MQ, NewtMQ, RabbitMQ, STOMP

 インフラ統括本部の大山裕泰です。以前に STOMP と rabbitmq-stomp について紹介しました。
 STOMP は AMQP などに比べて非常にシンプルなプロトコルのため、高速なメッセージ転送が行えると期待していまいたが、とてもイマイチな結果でした。
 「これはきっと実装の問題に違いない!」と推測し、ちょうど STOMP 1.2 対応の C 実装サーバも知られていなかったので、STOMP プトロコルによる高速なメッセージ転送を行う MQ サーバ NewtMQ (にゅーとえむきゅー) を C で実装してみました。

 - https://github.com/newtmq/newtmq-server

 こちら の簡易ベンチマークツールを使って、大量のメッセージを送受信するのに要する時間を計測しました。
 

 
 こちらの図は、それぞれのサーバで 64 ~ 32K のサイズのメッセージを 10 万回送受信する処理が完了するまでにかかった時間を比較しています。
 また、全てのブロックサイズにおける転送時間の平均において、送信処理と受信処理の内訳を表した図が以下になります。青が送信処理時間、緑が受信処理時間を表しています。
 

 
 NewtMQ が 4 割弱ほど RabbitMQ (AMQP) より早い結果になりました。
 Kafka の結果が悪いのは、Publisher から送られたメッセージをストレージに格納するために他の結果と比べて処理に時間がかかっているものと考えられます。
 なお、Kafka のベンチマークでは Publisher の ‘acks’ パラメータ の値を 0 に設定しています。’acks’ パラメータによって Publisher が送信したメッセージに対する応答メッセージ (Ack) を待ち受けるかどうかを設定します。Kafka Protocol 0.8.x までは、デフォルトで Ack を無視していましたが、0.9.0 からはデフォルトで Ack を待ち受けるようになりました。STOMP では、同様の確認応答を無視しており公平を期するために、ここでは ‘acks’ パラメータを明示的に 0 に設定しています。

 ここでは STOMP も Kafka も送信処理は確認応答を受け取らない設定をしている(リクエスト投げっぱなし)なので、むしろ重要なのは受信処理時間です。以下に、各ブロックサイズでの受信処理時間の結果を示します。

 メッセージサイズが小さいケースにおいて、Kafka が圧倒的に早いです。しかしメッセージサイズに比例して処理時間が大きくなります。恐らく Kafka のログ (メッセージを保持する箱) がファイルシステムのページキャッシュに乗っているケースでは高速になり、メッセージサイズが大きくなるにつれてページキャッシュに乗り切らなくなり、処理時間が増えているものと思われます。

 尚、検証した環境は以下のとおりです。
 
サーバスペック

CPU : Intel Core i7-6700 CPU @ 3.40GHz
RAM : 32GB
HDD : Intel SSD (512GB)
OS : Ubuntu14.04
rabbitmq-server : 3.6.1-1
rabbitmq-stomp : 3.6.1
Kafka : 2.11-0.9.0.1

クライアントスペック

CPU : Intel Core i5 @ 2.7 GHz
RAM : 16GB
Ruby : 2.2.2p95
stomp (RubyGem) : 1.3.5
bunny (RubyGem) : 2.3.1
ruby-kafka (RubyGem) : 0.3.6

 ただここで得た結果は、それぞれのソフトウェアの特徴のごく一部を切り出して比較したにすぎません。これによって NewtMQ が RabbitMQ や Kafka より良いということは決して言えません。
 ここで示した一部の性能は NewtMQ よりも Kafka の方が上回っていますし、RabbitMQ や Kafka が積み上げてきた実績には NewtMQ は遠く及びません。また機能面でも大きな差があります。

 Kafka ではクラスタによるハイアベイラブルでスケーラブルな環境 を組むことができ、またメッセージをストレージに格納させることで大容量のデータをキューに滞在させ、それらを高速に処理させることができます。
 また RabbitMQ は柔軟なメッセージルーティングを実現する機能性に加え、AMQP の実装としての高い実績を有しており、また rabbitmq-stomp も topic 転送や AMQP キューとの連携などといった多数のオリジナル拡張機能を有した高い機能性を持っています。
 更に、分散システムの中核を成す MOM においては高い信頼性と拡張性が要求されます。RabbitMQ ではメッセージの永続化、及び Lazy Queueクラスタリング 機能によってこうした要求に応えています。
 他にも 遠隔ノードへのメッセージ転送動的なメッセージルーティング など、メッセージ転送に関する様々な機能を提供してくれています。
 
 しかし実際に利用しているユーザ側としては「あるサービスから別のサービスにメッセージが正しく、早く、安定して転送できればそれで良い(他には特にいらん)」と考えるユーザが大部分なんじゃないかということを個人的な実感として思うようになりました。
 こうしたサプライヤー側の過剰とも言えなくもない機能やサービスは MOM の分野に限ったことではなく SDN やクラウドなどの分野においても個人的な実感として感じます。
 もちろん、ユーザ側の要件が複雑化していった際にはこうした恩恵にあずかるわけですが MOM の分野においては、ネットワークやストレージ、データベースなどと比べれば、いざダメだとなった時の乗り替えは比較的容易かと思います。

STOMP は柔軟性や機能性に欠けるプロトコルですが、広帯域・高信頼なネットワークに閉じた環境において、高速なメッセージの受け渡しを実現することができると踏んでおり、これを実現するために NewtMQ を作りました。今回の結果にって、こうした可能性が見えてきたんじゃないかと思っています。
(とはいえ、まだ全然使えるシロモノには仕上がっていませんので、メジャーバージョンが出るくらいまでは、どうか暖い目で見ていてくださいmm)

PAGE TOP