2013年2月アーカイブ

I just released Module::Build::Pluggable::CPANfile to CPAN.

Module::Build::Pluggable::CPANfile is plugin for Module::Build::Pluggable to include dependencies from cpanfile.

cpan: https://metacpan.org/release/Module-Build-Pluggable-CPANfile
github: https://github.com/kazeburo/Module-Build-Pluggable-CPANfile

This is Module::Install::CPANfile for Module:Build.

Here is short sample cpanfile and Build.PL.

# cpanfile
requires 'Plack', 0.9;
on test => sub {
    requires 'Test::Warn';
};

# Build.PL
use Module::Build::Pluggable (
    'CPANfile'
);

my $builder = Module::Build::Pluggable->new(
    license              => 'perl',
    dynamic_config       => 0,
    no_index    => { 'directory' => [ 'inc' ] },
    name        => 'Foo-Bar',
    module_name => 'Foo::Bar',        
    test_files => (-d '.git' || $ENV{RELEASE_TESTING}) ? 't/ xt/' : 't/',
    recursive_test_files => 1,
    create_readme  => 1,
    create_license => 1,
);
$builder->create_build_script();

Prereqs will be dumped into (MY)?META.(yml|json)

Try it!

Module::Install::CPANfileと同じ事をModule::Buildでもやりたい

Build.PL

use Module::Build;
use Module::CPANfile;

my $file = Module::CPANfile->load("cpanfile");
my $prereq = $file->prereq_specs;

my $build = Module::Build->new(
    license              => 'perl',
    dynamic_config       => 0,
    configure_requires   => $prereq->{configure}->{requires},
    build_requires       => {
        $prereq->{build} ? %{$prereq->{build}->{requires}} : (),
        $prereq->{test} ? %{$prereq->{test}->{requires}} : (),
    },
    requires             => $prereq->{runtime}->{requires},
    no_index    => { 'directory' => [ 'inc' ] },
    name        => 'Foo-Bar',
    module_name => 'Foo::Bar',

    test_files => (-d '.git' || $ENV{RELEASE_TESTING}) ? 't/ xt/' : 't/',
    recursive_test_files => 1,

    create_readme  => 1,
    create_license => 1,
);
$build->create_build_script();

cpanfile

requires 'LWP::UserAgent' => '6.02';
requires 'HTTP::Message'  => '6.04'; 

on 'test' => sub {
   requires 'Test::More'     => '0.98';
};

on 'configure' => sub {
   requires 'Module::Build'     => '0.38';
   requires 'Module::CPANfile' => '0.9010';
};

Build.PLがModule::CPANfileに依存するようになるのだが、META.{yml,json}のconfigure_requires(CPAN Meta Spec 2では {prereqs}->{configure})にModule::CPANfileが追加されるので事前に導入されてなくても自動で依存解決され、モジュールのインストールができるはず。ただし、cpanm 1.6002以上じゃないとCPAN Meta Spec 2の{prereqs}->{configure}に対応していないので注意です

追記

Module::Buildは0.3800からCPAN Meta Spec 2 でMETA.jsonを書き出すようになってます。ただしMETA.ymlは今まで通りのMeta Spec 1.4になっています。

YAMLがSpec 1.xで、JSONがSpec 2なのは後方互換性のためのようです。Spec 2ではJSONが優先フォーマットなので新しいツールはMETA.jsonを使い、古いツールはMETA.ymlしかみません。

cpanmの 1.5_11 で META.jsonを読み込む変更が入った際に Spec 2の prereqsではなくconfigure_requiresを参照していた問題がありましたが、1.6002 で修正されたので今は安心して使えます

数字を出力するコマンドを定期的に実行して、秒間の変化量を memcachedプロトコルで取れるサーバを書いた」で紹介した、derived。memcahcedプロトコルで外からアクセスするだけじゃなくて、GrowthForecastに直接ポストできたら便利だなと思ったので、構成見直してプラグインでデータの出力方法をカスタマイズできるようにした。

CPAN: https://metacpan.org/release/App-derived
github: https://github.com/kazeburo/App-derived

今まで

$ derived -i 10 --port 12306 cmdsfile

だったのが、

$ derived -i 10 -MMemcahced,port=12306 cmdsfile

と -M でプラグインを読み込むようになります。何もプラグインを指定しないと、Memcachedプラグインが読み込まれるので今までのオプションでも問題なく動きます。

GrowthForecastは

$ derived -i 60 -MGrowthForecast,api_uri=http://host/api/,service=foo,section=bar,persec=persec,interval=60 cmdsfile

のように指定すれば、1分毎にコマンドを実行して秒間の差分をGrowthForecastに投げる事ができます。

コンソールに出力するだけのプラグインもあります

$ derived -i 1 -MDumper,interval=1 cmdsfile

derivedを使ったJavaのヒープのモニタリング

これを使って某サービスのとあるJavaプロセスのヒープをGrowthForecast使ってグラフにしてみた。Javaなプロセスのヒープの簡単な統計は jstat というコマンドで取得できます。プロセス側のオプションを変更せずに使えるのがいいところ。

$ jstat -gcutil -h10 $pid 1000
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00   0.00  73.66  43.10  51.80    142    3.402   108   10.482   13.884
  0.00   0.00  73.66  43.10  51.80    142    3.402   108   10.482   13.884

とりあえず取りたいのはOLD領域の使用率(解放できないメモリが溜まると100%に張り付く)と、FullGCの回数(メモリ不足になると回数が急増する)の2つ。

cmdsfileには

oldheap:/usr/local/java/bin/jstat -gcutil -h10 `pgrep -of 'foo.bar'` |tail -1|awk '{print $4*100}'
fullgc:/usr/local/java/bin/jstat -gcutil -h10 `pgrep -of 'foo.bar'` |tail -1|awk '{print $8*100}'

それぞれの数字を取得して、100倍にしている。これはGrowthForecastが小数をサポートしないためです。

そしてderivedを起動

derived \
    -i 60 \
    -MGrowthForecast,api_url=http://gf/api/,service=foo,section=bar,match_key=oldheap,prefix=${MYHOSTNAME}_,interval=60 \
    -MGrowthForecast,api_url=http://gf/api/,service=foo,section=bar,match_key=fullgc,type=persec,prefix=${MYHOSTNAME}_,interval=60 \
    /path/to/derived/cmdsfile

match_keyというGrowthForecastプラグインのオプションをつかって、上はoldheapの最新の値だけを送り、下はFullGCの秒間の差分をGrowthForecastに送ります。こんな感じで1つのプラグインを異なるオプションで複数指定する事もできたりします。

更新間隔はGrowthForecastなので1分で十分です。

そして出来たのがこんなグラフ。

derived2gf.png

元データで100倍しているので、GrowthForecast側で1/100にして表示しています。10m gc/secというのは1分間に1回程度はFullGCが動いているという感じですかね、丸められてしまっているので分かりにくいですが傾向は掴めそう。これを参考にサービスを安定させて、枕を高くして寝たい。

参考文献

Javaなサーバを見るときは、いつもここを参考にしてます

Javaメモリ、GCチューニングとそれにまつわるトラブル対応手順まとめ - 日記のような何か

@IT:チューニングのためのJava VM講座(後編)

新しい機能をリリースした際に、MySQLに対して効率的ではないクエリが発行されてしまって、それが積もってサービス全体に影響が出てしまう前に発見してアラートをあげたい。

発見する手立てとしてはCPU使用率やInnoDBのROW OPERATIONSが考えられるところですが、今回はスロークエリが発生した回数を監視することにした。ちなみにいつものことながら対象とするMySQLは4.0系。long_query_timeがオンラインで変更できません。。。はい

MySQLのスロークエリが発生した回数は、show status のSlow_queriesという項目でみることができて

mysql> show  status like 'Slow_queries';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries  | 7238  |
+---------------+-------+
1 row in set (0.00 sec)

のように起動してから今までの long_query_time を超えた回数が表示できます。今回やりたい監視は、この数字を一定時間毎に取得して、その増加分の秒間の平均が閾値を超えていたらアラートをあげるというもの。

Nagiosでこのようなカウンター系の数字を監視をするには、プラグインの中で取得したデータを別の場所に保存し、前回のデータと比べて差分を出す必要があります。既成のNagiosプラグインで差分が取れるのは、Percona Monitoring Pluginsに含まれる pmp-check-mysql-status などがあります。

Nagios以外だとzabbixやcactiなどのメトリクス収集ツールでアラートを上げられるのでしょうか。

今回は、もう少し汎用的に使える derived というサーバに入れるデーモンと、memcachedの任意のキーをチェックできる check_memcached_val.pl という Nagios プラグインを書いて監視をしてみました。

derived

derivedは指定したコマンドを定期的に実行して、その結果の差分をmemcachedプロトコルで取得できるようにするデーモンプログラムです

CPAN: https://metacpan.org/release/App-derived
github: https://github.com/kazeburo/App-derived

インストールは cpanm で

$ cpanm -n App::derived

次にコマンドを記したファイルを用意

slowqueries: mysql -NB -e 'show /*!50002 global */ status like "Slow_queries"'

「memcahcedのキー:コマンド」というフォーマットで書けます。複数指定する事もできます

起動

$ derived --port 12306 -i 10 /path/to/cmdfile

-i がコマンドを実行する間隔。ここでは10秒間隔にしています。プロセスはdaemontoolsやsupervisord、upstartなどで管理するといいでしょう

telnet で確認

$ telnet localhost 12306
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
get slowqueries
VALUE slowqueirs 0 1
0
END
quit

現在のslow_queriesは「0」のようですね。なお起動直後は「0E0」という特別な値が返るようになっています

check_memcached_val.pl

check_memcached_val.pl はmemcachedサーバから指定したキーのキャッシュを取得してチェックできる Nagiosプラグインです。perlで書いてます

github: https://github.com/kazeburo/checkmemcachedval

perl-5.8系が入っていればインストールはコピーだけで動くはずです

$ curl https://raw.github.com/kazeburo/check_memcached_val/master/check_memcached_val.pl > check_memcached_val.pl
$ chmod +x check_memcached_val.pl
$ cp check_memcached_val.pl /path/to/nagios/libexec

基本的な使い方は

$ ./check_memcached_val.pl -H ホスト -P ポート -k キャッシュのキー -w warningの閾値 -c criticalの閾値

です。今回はderivedが起動しているTCPポートから、slowqueriesというキャッシュを取得するので、Nagiosの監視コマンドの設定は

define command{
    command_name    check_slowqueries
    command_line    $USER1$/check_memcached_val.pl -H $HOSTADDRESS$ -P 12306 -k slowqueries -w 0.2 -c 0.4
}

のように定義しました。とりあえず1秒間に0.2回でwarning、0.4回でcriticalを閾値にしました。

あとは、サーバ毎のサービス設定を追加すれば設定完了です。これでNagiosでSlow_queriesの監視ができるようになりました。他に監視したい項目が増えても、同じ要領でderivedのコマンドファイルにコマンドを追加して、check_memcached_val.plで監視すれば新規にスクリプトを書かなくてもいいので、楽ですね。

まとめ

derivedというデーモンとmemcachedの指定した値をチェックするNagiosプラグインでMySQLのSlow Queriesの監視ができました。これで偶発的に問題のあるアプリケーションが出てしまっても早期に発見ができ、積極的な開発と安定したサービス運用の両立させていくことができるでしょう。

2つのプログラムの詳細は個別に記事を書いています

数字を出力するコマンドを定期的に実行して、秒間の変化量を memcachedプロトコルで取れるサーバを書いた http://blog.nomadscafe.jp/2013/02/-memcached.html

memcached上の任意の値を監視できるnagiosプラグイン書いた http://blog.nomadscafe.jp/2013/02/memcachednagios.html

Nagios統合監視[実践]リファレンス (Software Design plus)
株式会社エクストランス 佐藤 省吾 Team-Nagios
技術評論社
売り上げランキング: 237,444

指定したコマンドを定期的に実行してmemcachedプロトコルで結果が取得できるサーバ - App::derivedとも組み合わせて使えるように、memcachedのキャッシュのキーを指定して、その値を監視できるNagiosプラグインを書きました。check_snmpと同様に変化・差分量が計算できる機能もついてます。

変化・差分監視はの利用イメージとしては、Webアプリケーションなどで外部APIにアクセスしてそれがエラーになった際にあるmemcachedサーバに対してincrを実行し、それをNagiosで監視、エラーの回数が一定回数を超えたら警告を上げるなどの利用方法が考えられます。

check_memcached_val.pl
https://github.com/kazeburo/check_memcached_val

Perl 5.8.5以降が入っている環境であれば、おそくらコピーすればうごくはずです。

使い方

取得した値が指定した範囲にあるか・ないかの監視

$ ./check_memcached_val.pl -H ホスト -P ポート -k キャッシュのキー -w warningの閾値 -c criticalの閾値

正規表現での監視

$ ./check_memcached_val.pl -H ホスト -P ポート -k キャッシュのキー -r foo

変化・差分量の監視、秒間のrateになります

$ ./check_memcached_val.pl -H ホスト -P ポート -k キャッシュのキー --rate -w warningの閾値 -c criticalの閾値

rateオプションを指定した場合、監視の結果をtempdir以下のファイルに保存します。ファイルが存在していない初回の監視では自動でOKとなります

オプション

-h, —help
ヘルプの表示
-H, —hostname=STRING
ホスト名・IPアドレス
-P, —port=INTEGER
ポート番号・デフォルトは「11211」
-k, —key=STRING
取得するキャッシュのキー
-s, —string=STRING
文字列一致の監視
-r, —ereg=REGEX
正規表現での監視。perlの正規表現が使えます
-R, —eregi=REGEX
正規表現での監視。大文字小文字無視
—invert-search
-s,-r,-Rの監視結果を反転します
-w, —warning=THRESHOLD
取得した値が数値の場合の監視閾値
-c, —critical=THRESHOLD
取得した値が数値の場合の監視閾値
-t, —timeout=INTEGER
memcachedサーバへ接続する際のタイムアウト。デフォルト「10」
—rate
変化・差分量監視を有効にする
—rate-multiplier=INTEGER
—rateで得られた数値に一定の係数を掛ける

THRESHOLDの形式は Nagiosのドキュメントを参照してください。
http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT

特別な値「0E0」

memcachedから「0E0」という文字列が得られた場合は、このプラグインは「OK」となります。監視対象で差分計算をしていてまだデータがそろっていない場合などに、0E0をsetしておくとアラートが上がらなくて済みます。App::derivedはデータの準備ができるまで「0E0」を返すようになっています

Nagiosのプラグインで、データ転送速度など、変化量・差分を監視するには、プラグインでどこかファイルに監視結果を保存しておいて比較するという手が使えると2つ前のエントリ書きましたが、 check_snmpでもそれがサポートされていたので試してみました。恥ずかしながら知りませんでした。

check_snmpの「—rate」というオプションがそれ。

 --rate
    Enable rate calculation. See 'Rate Calculation' below
 --rate-multiplier
    Converts rate per second. For example, set to 60 to convert to per minute

Rate Calculation:
 In many places, SNMP returns counters that are only meaningful when
 calculating the counter difference since the last check. check_snmp
 saves the last state information in a file so that the rate per second
 can be calculated. Use the --rate option to save state information.
 On the first run, there will be no prior state - this will return with OK.
 The state is uniquely determined by the arguments to the plugin, so
 changing the arguments will create a new state file.

さっそくこれを使って、とあるサーバのトラフィックを確認してみます。Byteをbitに直すため —rate-multiplier に 8 を指定しています

$ ./check_snmp -H 192.168.x.x -C community -P 2c -o ifHCOutOctets.3 --rate-multiplier 8 --rate -w 80000000 -c 90000000
No previous data to calculate rate - assume okay

初回なのでこのようなメッセージがでます。exitコードは「0」でOKとなります

つぎに適当に間隔を置いて同じコマンドを実行

$ ./check_snmp -H 192.168.x.x -C community -P 2c -o ifHCOutOctets.3 --rate-multiplier 8 --rate -w 80000000 -c 90000000
SNMP RATE OK - 71677347.53 | IF-MIB::ifHCOutOctets.3=71677347.53

「SNMP RATE OK - 71677347.53」、だいたい70Mbpsぐらいですが、閾値よりも低いのでOKとなりました。

閾値を変更してもう一度、オプションを変えると以前のデータは使わなくなるようです

$ ./check_snmp -H 192.168.x.x -C community -P 2c -o ifHCOutOctets.3 --rate-multiplier 8 --rate -w 70000000 -c 90000000
No previous data to calculate rate - assume okay
$ sleep 10 && ./check_snmp -H 192.168.x.x -C community -P 2c -o ifHCOutOctets.3 --rate-multiplier 8 --rate -w 70000000 -c 90000000
SNMP RATE WARNING - *77539599.53* | IF-MIB::ifHCOutOctets.3=77539599.53

WARNINGになりました。

なお、ファイルはこのサーバの場合「/usr/local/nagios/var/check_snmp」以下に保存されてました。これはnagiosのビルド時に決まるようです。

普通に便利ですね。

Nagios統合監視[実践]リファレンス (Software Design plus)
株式会社エクストランス 佐藤 省吾 Team-Nagios
技術評論社
売り上げランキング: 236,015

↑リファレンス的に使える良本です

固定フォーマットしかサポートしないけど、Plack::Middleware::AccessLogより軽い PM::AxsLogがLTSVフォーマット形式のログに対応しました

https://metacpan.org/release/KAZEBURO/Plack-Middleware-AxsLog-0.03

PM::AxsLogについては Perl 2012 advent calendar hacker に書いてます。

AxsLog - Yet Another AccessLog middleware - Perl Advent Calendar Japan 2012 Hacker Track

LTSVフォーマットについては次のページを参考にしましょう

Labeled Tab-separated Values

【今北産業】3分で分かるLTSV業界のまとめ【LTSV】 - naoyaのはてなダイアリー

使い方

ltsvオプションを有効にします。response_timeオプションも有効にしていると、takenという値が追加されます

use Plack::Builder;
builder {
    enable 'AxsLog', ltsv => 1, response_time => 1,
    $app;
}

出力されるログは次のようになります

host:127.0.0.1  user:-  time:[08/Feb/2013:14:17:53 +0900]   req:GET / HTTP/1.1  status:200  size:6  referer:-   ua:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17  taken:41
host:127.0.0.1  user:-  time:[08/Feb/2013:14:17:53 +0900]   req:GET /favicon.ico HTTP/1.1   status:200  size:6  referer:-   ua:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17  taken:36

目grepがcombinedに最適化されてると一瞬迷いそうです

Nagiosを使っていると、その瞬間の絶対値だけじゃなく、差分や変化量を取得してその数値を監視したくなりますよね。ならないとしたら、今すぐこの記事を読むのをやめて病院に行って下さい。

Nagiosで変化量を測定するには、プラグインの中で前の数値をどこかファイルにでも保存し、比較するという手があります。このcheck_bandwidthはその方法を取っているようです。

ただ、これだと汎用性があまりないので、監視対象サーバ側に定期的に指定したコマンドを実行して、その数値を外部から取得できるサーバを書いてみました。

CPAN: https://metacpan.org/release/App-derived *出てくるまで時間がかかるようです

github: https://github.com/kazeburo/App-derived

使い方

インストールはcpanmで

$ cpanm -n App::derived

コマンドとキーを書いたファイルを用意します

slowlog: mysql -NB -e 'show global status like "Slow_queries"'
select: mysql -NB -e 'show global status like "Com_select"'

複数のコマンドを指定することができます。

derivedを起動します

$ derived -i 10 --port 12306 Cmd

-i がコマンドを実行する間隔、portがmemcachedプロトコルなサーバが使うポート、最後に上で書いたコマンドファイルを指定します

あとは memcached のクライアントでアクセスします。perlだとCache::Memcached::Fastでしょうか。

my $memcached = Cache::Memcached::Fast->new({
    servers => [qw/localhost:12306/],
});

say $memcached-server_versions->{"localhost:12306"};
=> 0.01
say $memcached->get('slowlog'); #秒間の数字だけ
=> 0.2
say $memcached->get('slowlog:full'); #JSON形式
=> {"persec":0.2,"exit_code":0,"last_update":1360224680,"status":"OK","raw":"Slow_queries\t5990"}

10秒間ごとにmysqlコマンドを実行し、Slow_queriesの値の差を10で割った数がでてきます。

あとは check_memcached_num.pl (仮称) というNagiosプラグインつくって監視すれば汎用性も高いし、便利なはず。こっちはのちほど

ずいぶん前から作って動かしてはいるのですが、GrowthForecastの他にもうひとつのグラフ作成ツールを公開しています。

https://github.com/kazeburo/HRForecast

デイリーで更新される数値のビジュアライズに使う事が出来ます。GrowthForecastにはない過去データの登録もできます。

モリスさんのスライドでも少し登場しています。弊社では主にHiveでの集計の書き出し先として使っています

http://www.slideshare.net/tagomoris/log-analysis-with-hadoop-in-livedoor-2013

画面はこんな感じ

hr_top.png

他のページにembedするHTMLの発行やCSVでダウンロードをサポートしています

データの登録APIについて

データの登録は、GrowthForecastと同じようにURIに対してPOSTリクエストを行います。

http://localhost:5127/api/:service_name/:section_name/:graph_name

に対して、numberとdatetimeのパラメータを付けてリクエストします。

とあるサービスの2013年2月6日のPCから閲覧数を登録したい時、

my $ua = LWP::UserAgent->new;
$ua->post('http://localhost:5127/api/awesomeservice/pv/pc', {
    number      => 1_000_000,
    datetime    => '2013-02-06 00:00:00'
});

となります。

HRForecastの最小の更新単位は1時間です。1時間未満のdatetimeは切り捨てられます。2013-02-06 16:23:40 は 16時のデータとして扱われます。

セットアップ方法

HRForecastはデータストアとしてMySQLを利用します

今のところソースコードはgithubから取得してください

$ git clone git://github.com/kazeburo/HRForecast.git

依存モジュールのインストール

$ cd HRForecast
$ cpanm -n -lextlib --installdeps .

MySQLにテーブル作成

mysql> create database hrforecast
$ cat schema.sql | mysql hrforecast

設定ファイルの編集

$ vim config.pl
{
    dsn => 'dbi:mysql:hrforecast;hostname=127.0.0.1',
    username => '', #dbのユーザ名
    password => '', #dbのパスワード
    port => '5127', #bindするport
    host => '127.0.0.1', #bindするIP
    front_proxy => [],
    allow_from => [],
};

環境に合わせて編集します。

そして起動

perl hrforecast.pl --config config.pl

ブラウザで指定したポートが見れればセットアップ完了です。

hr_total.png

こんな複合グラフも作れます

Plack::Middleware::ServerStatus::Lite のアクセス制御のところに IPv6 のアドレスが指定できるようになりました。kuriyamaさんありがとうございます

https://metacpan.org/release/Plack-Middleware-ServerStatus-Lite

使い方は今までと変わりません。

use Plack::Builder

builder {
    enable 'ServerStatus::Lite', 
        path => '/server-status',
        allow=> [ '127.0.0.1', '::1' ], 
        scoreboard => $dir;
};

のようにIPv4のアドレスに加えて、「::1」のようにIPv6のアドレスも追加することができます

ちなみにStarmanがIPv6に対応していて、IO::Socket::INET6 をインストールすることでIPv6のアドレスでもListenできます

Plack::Middlewareでリクエスト終了後になにがしかのか処理をしたい場合

sub call {
    my ($self, $env) = @_;
    my $t0 = [gettimeofday];
    my $res = $self->app->($env);
    my $ela = Time::HiRes::tv_interval($t0);
};

と書きそうになりますが、これだと $res が CodeRef になるStreaming形式のレスポンスでは正しく処理ができません。 Streaming形式のレスポンスは多くないだろうとか思ってると、Catalystがstreaming形式のレスポンスを返したりするので注意が必要です

そこで Plack::Util::response_cb を使うと楽です

sub call {
    my ($self, $env) = @_;
    my $t0 = [gettimeofday];
    my $res = $self->app->($env);
    Plack::Util::response_cb($res, sub {
        my $res = shift;
        sub {
            my $chunk = shift;
            if ( ! defined $chunk ) {
                my $ela = Time::HiRes::tv_interval($t0);
            }
            return $chunk;
        }
    });
};

リクエスト処理が終わると、$chunk が undefined になるのでそこで、ごにょごにょ処理します。

ただ response_cb はレスポンスのbodyにフィルタをかける用に作られているためか、Content-Length ヘッダが消されてしまいます。レアケースでContent-Typeを消したくない時、またresponse_cbをスキップして処理を高速化(ベンチマーク未実施)したい時は、responseが配列かどうか確認して処理してしまうのが良いでしょう

sub call {
    my ($self, $env) = @_;
    my $t0 = [gettimeofday];
    my $res = $self->app->($env);
    if ( ref($res) && ref($res) eq 'ARRAY' ) {
        my $ela = Time::HiRes::tv_interval($t0);
        return $res;
    }
    Plack::Util::response_cb($res, sub {
        my $res = shift;
        sub {
            my $chunk = shift;
            if ( ! defined $chunk ) {
                my $ela = Time::HiRes::tv_interval($t0);
            }
            return $chunk;
        }
    });
};

以上、Plack::Middleware::AxsLogServerStatus::Liteはこんな実装になってますよという話でした

Proclet::Declateでは今までも

use Proclet::Declare;
service('web', 'plackup -p 9413 app.psgi');
service('memcached', qw!/usr/local/bin/memcached -p 11211!);

と起動するコマンドを直接書けていたのですが、Procletのオブジェクトインターフェイスではコードリファレンスしか受け付けていませんでしたので、0.21をサポートしました。

https://metacpan.org/module/Proclet

コードリファレンスに加えて、文字列と配列のリファレンスでサービスを指定できます

use Proclet;

my $proclet = Proclet->new();

# 文字列
$proclet->service(
    tag => 'web',
    code => 'plackup -p 9413 app.psgi'
);

# 配列のリファレンス
$proclet->service(
    tag => 'memcached',
    code => [qw!/usr/local/bin/memcached -p 11211!]
);

# コードリファレンス
$proclet->service(
    tag => 'worker',
    code => sub { MyWorker->run }
);

$proclet->run;

Proclet::Declareやprocletコマンドもこっちの実装を利用するようにしてコードがすっきりしました。

あと最近のアップデートで、サービスを定義した順に起動するような変更も入っています。

どうぞご利用ください

Apacheを起動するときに使う事もある apachectl の -k restart は stop && start ではないので注意しましょう。
ServerLimitやThreadLimitなどの一部の設定は、restart では適用されず、stop && start が必要になります。

apachectl は実はshellscriptで出来ています。中をのぞくと

#!/bin/sh
..
HTTPD='../httpd'
..
start|stop|restart|graceful|graceful-stop)
    $HTTPD -k $ARGV
    ERROR=$?
    ;;

と書かれています。restartはhttpdコマンドにそのまま渡されるようです。

そこでhttpdコマンドのドキュメントを読むと詳しくは

Stopping Apache httpd
http://httpd.apache.org/docs/2.2/stopping.html

を見ろとあります。そのページの再起動のところを読むと

Signal: HUP
apachectl -k restart
Sending the HUP or restart signal to the parent causes it to kill off its children like in TERM, but the parent doesn’t exit. It re-reads its configuration files, and re-opens any log files. Then it spawns a new set of children and continues serving hits.

と書いてあります。子プロセスにTERMを送って終了させ、親プロセスはそのままで設定ファイルなどを読み直して子プロセスを生成する。実際のところリクエスト終了まで待たないgracefulとイメージした方がいいかもしれません

Apacheの再起動をしたいとき、自分は普段 apachectl を使わずに init.dスクリプトを使います

/etc/init.d/httpd (restart|graceful)

OSやinit.dスクリプトの書き方にもよるのでしょうが、多くの場合、restartは stop && start な実装になっていると思います。

Log::Minimalでデバッグログを出力する時は

$ENV{LM_DEBUG} = 1;
debugf("hello Boofy");

と環境変数 LM_DEBUG を使うのですが、実はこの環境変数名はカスタマイズ変更可能で

local $Log::Minimal::ENV_DEBUG = "MYAPP_DEBUG";
$ENV{MYAPP_DEBUG} = 1;
debugf("hello Boofy");

と、$ENV_DEBUG を書き換えることで別の環境変数を参照させることができます。

(0.14時点でドキュメントにないですが..)

先日出した Log::Minimal 0.14でこのカスタマイズにもうひとつの方法が追加されました。hirose31さんpullreqありがとうございます!

use Log::Minimal env_debug => 'MYAPP_DEBUG';
$ENV{MYAPP_DEBUG} = 1;
debugf("hello Boofy");

モジュールを読み込む際の引数に env_debug => 環境変数名 とすると、$ENV_DEBUG が上書きされた debugf と debugff がエクスポートされるようになります。

さっそくProcletで使っています

https://metacpan.org/source/KAZEBURO/Proclet-0.20/lib/Proclet.pm

Procletは PROCLET_DEBUG という環境変数を有効にすると Proclet 自身のデバッグログを出力できます。

なお、この変更で、Exporterを継承しなくなったので

Log::Minimal->export_to_level(..)

が使えなくなりました。ぐぐるといくつか利用例があるようなのでご注意ください

GrowthForecastをCPANにリリースしました

https://metacpan.org/release/GrowthForecast

インストールも

cpanm GrowthForecast

だけで完了します。

それから、このバージョンからインターフェイスやドキュメントのページが英語に変わってます。絶賛添削pullreq募集中です