q4m にまつわる適材適所
jdbc で接続するのはちょっと・・・という意見が出たので、一個ラッパーを挟むことにして、無事職場で導入されそうだ。
ラッパーは apache の mod で作成した。単純にキュー投入リクエストを受け取り q4m に投入するだけの簡単なもの。とはいえ内部で mod_dbd を使用したコネクションプーリングも行い、(apache 上で動作させているため)ログ出力等の周辺機能もプラガブルに追加、変更可能だ。apache を使わずこれらを作り込むのは結構大変だ。
これでキューへのデータ投入に関しては単純な http リクエストで行えるということになる。
php でも servlet でも cgi でも簡単に作れるこの機能をあえて apache の mod で作成したのは、パフォーマンスを一番期待できるのではと考えてのこと。ラッパーがボトルネックになることは目に見えていたが、最小限に止めたかったのだ。負荷テストの結果もまずまず問題無い数字が出て一安心した。
この apache と q4m は同一サーバ上に置いてもらい、apache - q4m 間は unix ソケットで通信させることにした。(ネットワークがボトルネックになるのを避けるため)
で、ここまでが前置き。この機能を説明した際に、汎用性についての質問を受け、若干嫌な予感がした。汎用性は意識して作ったが、q4m は単なるキューなのだ。いくら便利でもゴミ箱にはしてもらいたくない。
現在、memcached の導入も別プロジェクトで進んでいるが、これらの製品は基本的にネットワーク経由で使用するものだ。
ネットワークを経由するというのは非常に重たい処理である。インプロセスで動作するキューやキャッシュが使用可能であれば、これらの製品に比べ最低でも2桁は上のパフォーマンスが確保できるだろう。単純なキューでいいのであれば、
final int cnt = 1000 * 1000 * 10; // 1000 万件 long start = System.currentTimeMillis(); List<String> data = new ArrayList<String>(cnt); for (int i = 0; i < cnt; i++) { data.add("data" + i); } System.out.printf("Prepare Elapsed:%5d ms size=%d \n", System.currentTimeMillis() - start, data.size()); Queue<String> queue = new ArrayBlockingQueue<String>(cnt); start = System.currentTimeMillis(); for (int i = 0; i < cnt; i++) { queue.add(data.get(i)); } System.out.printf("Put Elapsed:%5d ms size=%d \n", System.currentTimeMillis() - start, queue.size()); start = System.currentTimeMillis(); for (int i = 0; i < cnt; i++) { queue.poll(); } System.out.printf("Poll Elapsed:%5d ms size=%d \n", System.currentTimeMillis() - start, queue.size());
結果:
Prepare Elapsed:19680 ms size=10000000 Put Elapsed: 609 ms size=10000000 Poll Elapsed: 374 ms size=0
毎秒 2000 万件くらい処理できることになる。memcache に比べて 3 桁、物理ディスクへの書き込みを伴う q4m に比べると 3 桁 から 4 桁違うことが分かる。
ということで、q4m に関しては便利に使う環境は整った。次のステップとしては「いかにして q4m を使わずに処理をインプロセスで済ませるか」ということを考える必要があると思う。
追記 2009-07-30:
間違った用途には使われていないが、おかげさまでいまや q4m はウチの職場で無くてはならないものになっている。奥さんに感謝・・・。