q4m にまつわる適材適所

サイボウズ奥さん作の q4m を職場で提案。


jdbc で接続するのはちょっと・・・という意見が出たので、一個ラッパーを挟むことにして、無事職場で導入されそうだ。


ラッパーは apache の mod で作成した。単純にキュー投入リクエストを受け取り q4m に投入するだけの簡単なもの。とはいえ内部で mod_dbd を使用したコネクションプーリングも行い、(apache 上で動作させているため)ログ出力等の周辺機能もプラガブルに追加、変更可能だ。apache を使わずこれらを作り込むのは結構大変だ。


これでキューへのデータ投入に関しては単純な http リクエストで行えるということになる。


php でも servlet でも cgi でも簡単に作れるこの機能をあえて apache の mod で作成したのは、パフォーマンスを一番期待できるのではと考えてのこと。ラッパーがボトルネックになることは目に見えていたが、最小限に止めたかったのだ。負荷テストの結果もまずまず問題無い数字が出て一安心した。


この apacheq4m は同一サーバ上に置いてもらい、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 はウチの職場で無くてはならないものになっている。奥さんに感謝・・・。