2009年06月26日

Apacheのmod_cacheのキャッシュ保存先にmemcachedが使えればいいのにと長年思ってきましたが、mod_libmemcached_cacheがそれを実現してくれました。
しかも、libmemcachedを利用しているので、性能も高く、またConsitent Hashingも使えますし、バイナリプロトコルもばっちりです。

mod_libmemcached_cache.png


図にするとこんな感じ。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数はどうなるのかなど検証がもうちょっと必要です。

2009年06月16日

偶数月と言えばWEB+DB PRESS。「WEB+DB PRESSで連載やります」の2回目がVol. 51に掲載されます!

内容は、予告通りインフラ周りのことが書かれています。ハードウェアやネットワークなどアプリケーション開発者にとって普段あまり目にしないことなどが書かれていますので、ぜひ読んで頂けたらと思います。

と、言いながら実は書いたのが僕ではないという、変わり身の術です。
餅は餅屋、今回はインフラ担当のエンジニアに書いてもらいました。
その分内容も濃くなっていると思いますので、ぜひお手に取ってくださいませ。


詳しい目次等は技術評論社様のサイトで見れます。
http://gihyo.jp/magazine/wdpress/archive/2009/vol51

2009年06月01日

perlのmemcachedクライアントのCache::Memcached::Fastの最新バージョン 0.14 にはちょっとしたラップがあります。
Consistent-Hashing分散アルゴリズムのketamaのアルゴリズムが改善されて、それまでのバージョンと互換性がなくなっています。
changesにも書いています

- improve Ketama distribution. The change is incompatible
with Ketama implementation in previous versions.


現在サービスにCache::Memcached::Fastの 0.13 以下でketamを利用している場合、バージョンアップした瞬間にキャッシュがほどんどhitしなくなるってわけです。
ちょっっ、って訳で、patchを作った。

Cache::Memcached::Fast 0.14を以前のketama algorithmにも対応させる
(gistにも http://gist.github.com/121283 )

先週末、Cache::Memcached::Fastの作者のTomashにこのpatchを送ったところ、必要性は理解してもらえたけど、「これが便利なのは一部のユーザの一度の移行の時だけだよねー」(意訳)と言われました。


そりゃそーだ


というわけで、このエントリーを書いて再度紹介することにしました。


現在Cache::Memcached::Fastの0.13以下をketamaで利用していて、0.14を使いたいが移行に関して困っている人は、上記のpatchをあてた上で、malaさんのCache::Migrateを利用すると良いかと思います。

具体的には、新旧2つのインスタンスを作ります。新しいキャッシュはネームスペースなどを使って分けるとサーバを追加するなど要らないかと。

use Cache::Migrate;
use Date::Parse;
use Cache::Memcached::Fast;

my $old_cache = Cache::Memcached::Fast->new({
   servers => ["127.0.0.1:11211"],
   old_ketama_algo => 1,
});
my $new_cache = Cache::Memcached::Fast->new({
   servers => ["127.0.0.1:11211"],
   namespace => 'new:'

});

my $cache = Cache::Migrate->new(
     { cache => $old_cache, expires_on => str2time("2009/05/15 00:00:00") },
     { cache => $new_cache },
 );


ということで、速度も高速なまま、アルゴリズム改善されたCache::Memcached::Fast v0.14をenjoy!しませう

2009年04月20日

WEB+DB PRESS Vol.50から、「大規模Webサービスの裏側」というタイトルでmixiのシステム運用について連載を書かせて頂くことになりました。
初回は一つ前にblogでもなんとなく触れた画像配信について書きましたので、ご興味をもった方はぜひ読んでくださいませ。

詳しい目次等は技術評論社様のサイトで見れます。
http://gihyo.jp/magazine/wdpress/archive/2009/vol50

2009年04月13日

Net::Squid::Purgeというモジュールがでてたので。

Squidのcacheの消し方は、Squidから特定のキャッシュのみを削除する方法にsquidclientを使ったやりかたが紹介されているのですが、上のモジュールはそれをPerlから便利に使えるようにしたもの見たいですね。

主にReverse Proxyとして使っているパターンですが、最近ではsquidclient使ったり、IO::Socketやtelnetで叩くのが面倒なので、HTTPの仕様を利用しています。

HTTP/1.1の仕様でキャッシュサーバは

 Cache-Control: max-age=n

のようなリクエストを受け付けた場合に、キャッシュの経過時間がn秒よりも古かった場合、expires等が切れてなくても、オリジンのサーバにリクエストを行うことになっています。

そこでこれを利用して

Cache-Control: max-age=1

を付けてリクエストを行なえば、だいたい常にオリジンのサーバへ確認をしてくれます。
オリジナルコンテンツが無くなっていた場合は、元のキャッシュが消えて、404がキャッシュされるような感じですね。なので厳密なキャッシュ削除とはちょっと違います。
なんで、max-ageが 0 ではなくて、1 なのかというと、squidの設定で、ignore-no-cacheがあるためです。このオプションが有効になっている場合、Cache-Control: no-cacheやPragma: no-cacheと同じように、max-age=0は無視されてしまします。


このリクエストを実際に運用しているサーバに大量に送ると負荷になってしまう可能性があるので、他人/他社のWebサービスに対しては行なわないようにしてください。

2008年12月12日

このページにあるようなjpegファイルがImage::Size(3.1.1)でうまく認識ができないので対応patch書きました。

WX310SA(など)で映した写真の場合、jpegのヘッダ中でmarkerであるffが並ぶところがあるようで、Image::Sizeでそれを扱えません。ただ、普通の場合、Image::SizeがImage::Magickを呼ぶので気付きにくいかもしれません。サービスで使う時に、Image::Magickを呼ばれたくないので無理矢理切っていたため気付いた次第です。

↓patch

--- lib/Image/Size.pm.orig      2008-12-12 18:13:39.000000000 +0900
+++ lib/Image/Size.pm   2008-12-12 18:18:53.000000000 +0900
@@ -932,12 +932,19 @@
     &$read_in($stream, 2);
     while (1)
     {
-        $length = 4;
+        $length = 2;
         $segheader = &$read_in($stream, $length);
 
         # Extract the segment header.
-        ($marker, $code, $length) = unpack("a a n", $segheader);
+        ($marker, $code) = unpack("a a", $segheader);
 
+        while ( $code eq $MARKER && ($marker = $code) ) {
+            $segheader = &$read_in($stream, 1);
+            ($code) = unpack("a", $segheader);
+        }
+        my $length_header = &$read_in($stream, 2);
+        $length = unpack( "n", $length_header);
+ 
         # Verify that it's a valid segment.
         if ($marker ne $MARKER)
         {


確認
patch前

$ perl -MImage::Size -le 'print join ",", Image::Size::imgsize($ARGV[0])' SA410081.jpg
960,1280,Joint Photographic Experts Group JFIF format

Image::Magickが呼ばれて、なんか長いフォーマットが表示されます

patch後

$ perl -Iblib/arch/auto -Iblib/lib -MImage::Size -le 'print join ",", Image::Size::imgsize($ARGV[0])' SA410081.jpg
960,1280,JPG

正しく"JPG"と判定できました

ExifToolだとProcessJPEGの中で対応していますね

            # JPEG markers can be padded with unlimited 0xff's
            for (;;) {
                $raf->Read($ch, 1) or last Marker;
                $nextMarker = ord($ch);
                last unless $nextMarker == 0xff;
            }


patchを送った方がいいかなぁ

2008年11月21日

パソナテック10周年記念 PTカンファレンスVol.6 インフラエンジニア討論会2008 ~インフラエンジニア進化論~に参加させて頂く事になりました。

内容は

今やITシステムは社会において欠かすことのできない重要なインフラとなりました。
その中で、サーバ/ネットワークエンジニアが担当する運用業務は日々高度化、多様化を遂げています。
当カンファレンスでは、インフラエンジニアにフォーカスして、最前線で活躍する著名なエンジニアをゲストに迎え、現場で生まれた失敗談・成功談そしてここだけの秘話を通して、インフラエンジニアという仕事の面白さ・やりがい・将来性を皆さんと討論していきたいと考えています。
ここだけしか聞けない現場の生の声が盛り沢山!是非ご参加ください。

<討論テーマ>
・インフラエンジニアって何だろう?
・大規模サイトの運用,スケールアップ・スケールアウト
・お勧め運用管理ツール/気になるあのサービス
・インフラエンジニアのあるべき働き方とは? など


になります。
2008/12/06(土) 14:00~なのでご興味ある方はぜひ、来てください。

2009年07月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

プロフィール

NICKNAME: kazeburo
FN: Masahiro Nagano
EMAIL:kazeburo@nomadscafe.jp
URI: mixi
Web Developer

最近のコメント

RSS feed meter for http://blog.nomadscafe.jp/
このblogのはてなブックマーク数