apache httpd のモジュールとして mod_load_monitor というのが作られました。
開発は @matsumotory さん、経緯などは下記からたどっていこう。
ちょっとだけ遊んでみました。
人間とウェブの未来 – ロードアベレージを監視して任意のコマンドを実行する(Apacheで)
httpdのリクエスト処理をフックしてサーバのLoadAverageを計測、閾値によりコマンド実行。
折角なので実行するコマンドの例示として、閾値Overのリクエストを個別に集計してみよう。
mod_load_monitorの設定
集計にはredisを使って任意のキーをincrementする方式でカウントしてみる。
LoadModule load_monitor_module /usr/lib/apache2/modules/mod_load_monitor.so
LoadMonitorOver 1.0 "/usr/bin/redis-cli -s /var/run/redis/redis.sock -n `date +%m` incr over_$LOAD_MON_FILENAME"
LoadMonitorUnder 1.0 "/usr/bin/redis-cli -s /var/run/redis/redis.sock -n `date +%m` incr under_$LOAD_MON_FILENAME"
本来必要なコマンドは redis-cli incl $key だけで、任意KeyのValueが数だったら1つ増やすという処理。これを下記要領で。
- 現在月をDBのindexにした、ラクチンな月ごと集計!
- system()を使っているのでdateの箇所に展開が使える
- とりあえずファイルパスをキーにカウント、prefixでover/underを判別
- 接続をUnixソケットに
redisをモニターしながらアクセス
アクセス毎にインデックス7番のDBに対してincrが発行されているのがわかる。
redis 127.0.0.1:6379[7]> MONITOR
OK
1343363853.354785 (db 7) "MONITOR"
1343363856.543896 (db 7) "SELECT" "7"
1343363856.544411 (db 7) "incr" "under_/var/www/favicon.ico"
1343363857.488522 (db 7) "SELECT" "7"
1343363857.488986 (db 7) "incr" "under_/var/www/favicon.ico"
1343363859.188487 (db 7) "SELECT" "7"
1343363859.189031 (db 7) "incr" "under_/var/www/5"
1343363859.378521 (db 7) "SELECT" "7"
1343363859.379157 (db 7) "incr" "under_/var/www/5/"
1343363859.648242 (db 7) "SELECT" "7"
1343363859.648668 (db 7) "incr" "under_/var/www/5/"
1343363860.273772 (db 7) "SELECT" "7"
1343363860.274207 (db 7) "incr" "under_/var/www/7"
1343363860.453436 (db 7) "SELECT" "7"
1343363860.453912 (db 7) "incr" "under_/var/www/7/"
1343363860.628093 (db 7) "SELECT" "7"
1343363860.628502 (db 7) "incr" "under_/var/www/favicon.ico"
キーが作られている様子。
> keys under*
1) "under_/var/www/5/"
2) "under_/var/www/5"
3) "under_/var/www/7/"
4) "under_/var/www/favicon.ico"
5) "under_/var/www/7"
キーの中身、カウントになっている。
> mget under_/var/www/favicon.ico under_/var/www/7/
1) "3"
2) "1"
最初からログをMapReduceしたようなKey/Valueになります。
実際にカウントするならVirtualHost名でやるのが一番使いでがありそう。
パフォーマンスへの影響
当たり前のように甚大です。軽くabしてみます、多重5の2000リクエスト。
まずredisフックなし、2450rpsほど。
Concurrency Level: 5
Time taken for tests: 0.816 seconds
Complete requests: 2000
Failed requests: 0
Write errors: 0
Total transferred: 1404000 bytes
HTML transferred: 850000 bytes
Requests per second: 2450.81 [#/sec] (mean)
Time per request: 2.040 [ms] (mean)
Time per request: 0.408 [ms] (mean, across all concurrent requests)
Transfer rate: 1680.14 [Kbytes/sec] received
Redisフック、273rpsとなった、軽く10%程度にまで落ち込む。
Concurrency Level: 5
Time taken for tests: 7.300 seconds
Complete requests: 2000
Failed requests: 0
Write errors: 0
Total transferred: 1404000 bytes
HTML transferred: 850000 bytes
Requests per second: 273.97 [#/sec] (mean)
Time per request: 18.250 [ms] (mean)
Time per request: 3.650 [ms] (mean, across all concurrent requests)
Transfer rate: 187.82 [Kbytes/sec] received
一応シンプルにファイル追記にした場合は450rpsほどでした。
LoadMonitorUnder 1.0 "echo $LOAD_MON_FILENAME >> /tmp/under"
お遊び色のモジュールに便乗してやってみたネタなのでこんなもんで。
credisでやったらちゃんと早いんだろうな。
追記: 集計をまとめる
redis上でkeysからhashに移せば hgetallでひとまとめのJsonとして受け取れる。
MapReduceのようだ。
> r = Redis.new
> r.select 7
> r.keys.each { |k| r.hset("2012-07", k, r.get(k)) }
> puts JSON.pretty_generate(r.hgetall("2012-07") )
< {
< "under_/var/www/5/": "2",
< "under_/var/www/5": "1",
< "under_/var/www/7/": "1",
< "under_/var/www/favicon.ico": "3",
< "under_/var/www/7": "1",
< "under_/var/www/3/": "2000"
< }