mod_libmemcached_cacheでApacheのcacheをmemcachedに保存する
Apacheのmod_cacheのキャッシュ保存先にmemcachedが使えればいいのにと長年思ってきましたが、mod_libmemcached_cacheがそれを実現してくれました。
しかも、libmemcachedを利用しているので、性能も高く、またConsitent Hashingも使えますし、バイナリプロトコルもばっちりです。
図にするとこんな感じ。revserse
proxyのcacheがmemcachedになるので、cache効率が上がり、またApplicationサーバからも同じmemcachedが参照できるのでcacheを変更したりできるかもしれません。
導入
mod_libmemcached_cacheはgithubから入手できます
http://github.com/agentzh/mod-libmemcached-cache/tree/master
git clone git://github.com/agentzh/mod-libmemcached-cache.git cd mod-libmemcached-cache
ビルドには、Apacheのソース(mod_cache.h)とlibmemcachedが必要です。
libmemcachedはrpmもあるのでさくっと導入できると思います。
あと、若干のバグ(レスポンスが1byte多い)とlibmemcachedのオプションを指定したかったので以下のpatchを当てました。
diff --git a/mod_libmemcached_cache.c b/mod_libmemcached_cache.c index ca39cd8..8042f17 100644 --- a/mod_libmemcached_cache.c +++ b/mod_libmemcached_cache.c @@ -295,7 +295,7 @@ static int open_entity(cache_handle_t *h, request_rec *r, const char *key) { /* Found the data file */ found_body = 1; lobj->body = ret_val; - lobj->body_len = ret_val_len + 1; + lobj->body_len = ret_val_len; } else { /* ret_key[ret_key_len - 1] = '\0'; */ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, @@ -614,7 +614,11 @@ static int libmem_cache_post_config(apr_pool_t *p, apr_pool_t *plog, "Found %d memcached servers.", memcached_server_count(sconf->memc)); //memcached_behavior_set(sconf->memc, MEMCACHED_BEHAVIOR_VERIFY_KEY, 1); - //memcached_behavior_set(sconf->memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1); + memcached_behavior_set(sconf->memc, MEMCACHED_BEHAVIOR_NO_BLOCK, 1); + memcached_behavior_set(sconf->memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, 1); + memcached_behavior_set(sconf->memc, MEMCACHED_BEHAVIOR_DISTRIBUTION, MEMCACHED_DISTRIBUTION_CONSISTENT ); + //memcached_behavior_set(sconf->memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1); + memcached_behavior_set(sconf->memc, MEMCACHED_BEHAVIOR_IO_KEY_PREFETCH, 1); return OK; }
make時にApacheのapxsと、ソースの場所を指定します
$ make APXS=/usr/sbin/apxs \ MOD_CACHE_SRC_DIR=/path/to/httpd/httpd-2.2.9/modules/cache
今回ちょっと面倒だったのでmake installはしていません。
Apacheから直接buildしたディレクトリを参照しちゃいました。
Apacheのhttpd.confには以下を追加しました。
memcachedは4台設定です
LoadModule cache_module modules/mod_cache.so LoadModule libmemcached_cache_module /path/to/mod-libmemcached-cache/.libs/mod_libmemcached_cache.so <IfModule mod_libmemcached_cache.c> LibmemCacheServers 127.0.0.1:11255,127.0.0.1:11256,127.0.0.1:11257,127.0.0.1:11258 CacheEnable libmemcached / </IfModule>
memcachedは今回1.4.0-rc1を使いました。
httpd.confで指定した、4台起動します。
$ memcached -p 11255 -u 0 -d $ memcached -p 11256 -u 0 -d $ memcached -p 11257 -u 0 -d $ memcached -p 11258 -u 0 -d
「-u 0」はUDPをListenしないためのオプションです。
設定ができたので、Apacheを起動します。起動方法は様々でしょう
$ apachectl -k start
確認
curlを使ってcacheがされているか確認します。test.jpgというファイルをDocumentRootに置いて、
$ curl -s -v -O /dev/null 'http://localhost:8888/test.jpg'
なんどかアクセスして、レスポンスのヘッダに
Age: 数字
が記録されていたら成功です。どこかのmemcachedにcacheされています。
本当にmemcachedに保存されているかは、memcachedを起動する時に、デーモンにせずに-vvオプションを付けておけばコンソールにログが流れるのでわかるでしょう。
ベンチマーク
ベンチマークは、abではなく、http_loadを使いました。http_loadだと複数のURLにアクセスができるのでmemcachedの分散を確認するのにもこちらを選択しました。
まずDocumentRootに、1.jpgから40.jpgまでファイルを作成します。今回は3.5KBのjpgを利用しました。ファイル名が異なるだけで中身はすべて同じです。
urls_fileにベンチマークで使うURLを書きます。
http://localhost:8888/1.jpg http://localhost:8888/1.jpg .. http://localhost:8888/40.jpg
このファイルを指定してhttp_loadを起動します。
$ http_load -verbose -parallel 8 -fetches 10000 urls_file
ベンチマーク結果
ベンチマーク結果です。
まず、mod_cacheを無効にした状態。直接配信
10000 fetches, 8 max parallel, 3.549e+07 bytes, in 1.49197 seconds 3549 mean bytes/connection 6702.53 fetches/sec, 2.37873e+07 bytes/sec msecs/connect: 0.184619 mean, 6.372 max, 0.018 min msecs/first-response: 0.706464 mean, 15.463 max, 0.365 min HTTP response codes: code 200 -- 10000
次に、mod_libmemcached_cacheを有効にした状態
verbose -parallel 8 -fetches 10000 urls 10000 fetches, 8 max parallel, 3.549e+07 bytes, in 1.06743 seconds 3549 mean bytes/connection 9368.33 fetches/sec, 3.32482e+07 bytes/sec msecs/connect: 0.13438 mean, 1.019 max, 0.017 min msecs/first-response: 0.488267 mean, 3.157 max, 0.129 min HTTP response codes: code 200 -- 10000
なんと、memcachedの方が1.5倍高速です!
直接配信の方は、diskにnoatimeオプションを付けていないという理由もありますが、これには驚きです。
今回のテストはApacheはpreforkで動かしていますが、workerの場合どうなるのか、
また、memcachedと接続するport数はどうなるのかなど検証がもうちょっと必要です。