Java

Twitterで使っているScalaで書かれたオープンソースのメッセージキューサーバー、Kestrel

Kestrelは大規模かつ高速に運用できるメッセージキューサーバーです。Twitterで使っています。
ソースはhttps://github.com/robey/kestrelよりチェックアウトできます。

・特徴
Kestrelは特徴として
– memcachedプロトコルをサポートしており、クライアントのプラットフォーム非依存
– Scalaで書かれており、高速なJVMの恩恵を受けることが出来る
– 全部で2500行ほどとシンプル
– 基本メモリベースで高速だがメッセージはファイルシステムにジャーナルが記録されており耐障害性が確保されている
– キューから取り出したメッセージをクライアントがacknowledgeするまで捨てないことで処理漏れを防ぐことができる
といったことが挙げられます。

・Memcachedプロトコル
Memcachedプロトコルの基本は非常に簡単で、setコマンドで値をセット、getコマンドで値の取得を行います。
Kestrelはメッセージキューサーバなので一度値を取得すると消えてしまうのが特徴です。
以下、Memcachedのプロトコルを簡単に説明した動画です。

左のターミナルがKestrelサーバ、右のターミナルがクライアント。telnetで直接memcachedプロトコルを喋ってます。
1. setコマンドでfooというキーでbarという値をセット
2. getコマンドでfooを取得
3. 再度getコマンドで取得しても値は空
というシナリオです。

・永続化
先の説明の通り、Kestrelはメッセージを永続化するのでサーバーが落ちて再起動してもメッセージをちゃんと取り出せます。
以下、動画によるデモ。

左のターミナルがKestrelサーバ、右のターミナルがクライアント。
1. setコマンドでfooというキーでbarという値をセット
2. Kestrelサーバをシャットダウン、再起動
3. telnetコマンドで再接続(サーバ起動まで何度か繰り返してます)
4. getコマンドで値が取得できる(サーバがジャーナルから値を復旧させた証拠)

・ブロックフェッチ
普通のMemcachedのようにキャッシュサーバとして使う場合は指定したキーの値が空であれば空だとすぐ返せばいいんですが、メッセージキューでるKestrelではキューが空の場合クライアントが繰り返しポーリングをするのは非効率的なのでメッセージがセットされれるまでクライアントをブロックさせるプロトコル拡張が行われています。
以下、ブロックフェッチのデモ。キー名に t=***** と追加するところがコツ。

左上がKestrelサーバ、右がクライアント1、左下がクライアント2
1. getコマンドでfooというキーを取り出してもすぐに空が帰ってくる
2. クライアント1で、get foo/t=10000000(1万秒末)コマンドでfooキューに値が入るのを待つ
3. クライアント2でfooキューに値をセット
4. ブロックフェッチしていたクライアント1で値がとれる(ポーリングの必要がない)

・リライアブルフェッチ
クライアントがメッセージを取得して、そのメッセージの処理が終わる前に落ちてしまった時にメッセージが消えてしまっては困ります。そこで、やはりMemcachedプロトコルに拡張を施しクライアントが確認のメッセージ(close)を送るまでサーバでメッセージを保留しておく機能があります。
以下デモ

左上がKestrelサーバ、右上がクライアント1、左下がクライアント2、右下がクライアント3
1. クライアント2、3でブロックフェッチかつリライアブルフェッチ
2. クライアント1でfooキューに値をセット
3. クライアント3に値が渡される
4. クライアント3がcloseをせずに接続を切る
5. クライアント2に値が渡される
6. クライアント2で取得した値を処理した旨(/close)と、次のメッセージをリライアブル&ブロックフェッチ
7. クライアント1でfooキューに値をセット
8. クライアント2で値が取得できる

・Kestrelのフォーカス外
KestrelはJava標準のJMSよりも簡単に利用でき、またサーバもクライアントもマルチプラットフォームという特徴がありますが一般的なMOMにできてKestrelに出来ないこともあります。

– メッセージの順序保証
Kestrelはいくらでも分散デプロイすることができ、超大規模に運用することができます。ただ、それぞれのサーバーが独立して動くので順序は保証されません。同じサーバーへ届いたメッセージは届いた順に配信されますが、クラスタ全体では「たいだい順序通り」に配信されることになります。

– トランザクション
Kestrelのメッセージ受信、配信処理はトランザクショナルではないのでデータベースやその他のリソースとの処理内容をアトミックに扱うことはできません。例えばメッセージを受信してacknowledge(closeコマンド)を送る前にクライアントが落ちた場合はメッセージが二度処理されることになります。

関連記事:
twitterでも利用されているメッセージキュー Kestrelを試す – ゆろよろ日記