<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>blog.nomadscafe.jp</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/" />
    <link rel="self" type="application/atom+xml" href="http://blog.nomadscafe.jp/feedb.xml" />
    <id>tag:blog.nomadscafe.jp,2009-07-12://1</id>
    <updated>2013-05-17T08:26:50Z</updated>
    <subtitle>湘南新宿ラインで通勤する Operations Engineer のBlog</subtitle>
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type 4.27-ja</generator>

<entry>
    <title>Monoceros が HTTP/1.1に対応しました &amp; nginx と組み合わせたベンチマーク</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/05/monoceros-http11-nginx.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.236</id>

    <published>2013-05-17T08:19:36Z</published>
    <updated>2013-05-17T08:26:50Z</updated>

    <summary>C10K対応Prefork型高速PSGI/Plackサーバの Monoceros...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>C10K対応Prefork型高速PSGI/Plackサーバの Monoceros をHTTP/1.1に対応させました。</p>

<p><a href="https://metacpan.org/release/Monoceros">https://metacpan.org/release/Monoceros</a><br />
<a href="https://github.com/kazeburo/Monoceros">https://github.com/kazeburo/Monoceros</a></p>

<p>MonocerosではHTTPのKeepAliveに対応して、大量の接続を捌く事ができますが、リリース時点ではHTTP/1.0 KeepAliveにしか対応していませんでした。しかし、nginxのupsream などでは、keepaliveを有効にしてHTTPセッションを使い回したい場合にHTTP/1.1が求められます。</p>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="monoceros-keepalive.png" src="http://blog.nomadscafe.jp/2013/05/17/monoceros-keepalive.png" width="542" height="209" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></span></p>

<p>以前このあたりの事をしらべてblog書いています</p>

<p>nginx-1.1.x で httpなupstreamにもkeepaliveができるようになったので検証してみた<br />
<a href="http://blog.nomadscafe.jp/2012/02/nginx-11x-httpupstreamkeepalive.html">http://blog.nomadscafe.jp/2012/02/nginx-11x-httpupstreamkeepalive.html</a></p>

<p>これではMonocerosが活かせないということで既に1.1に対応しているStarmanを参考にしつつ、HTTP/1.1対応しました。対応したのは次の機能</p>

<ul>
<li>KeepAlive</li>
<li>Transfer-Encoding: chunked (Request &amp; Response)</li>
<li>HTTP Pipelining</li>
<li>Expect</li>
</ul>

<p>これらに対応しつつ、今までのHTTP/1.0のKeepAliveも使えるようになっています。</p>

<p>ほとんど使われない機能ですが、HTTP Pipeliningにも対応していて</p>

<pre><code>my $body = 'OK 'x10;
sub {
    [200, ['Content-Type'=&gt;'text/plain'],[$body]]
}
</code></pre>

<p>こんなpsgiを書いてMonocerosを起動してncを使って2つのリクエストを続けて投げてみると、</p>

<pre><code>$ cat reqs.txt 
GET /1 HTTP/1.1
Host: foo

GET /2 HTTP/1.1
Host: foo
Connection: close

$ cat reqs.txt | nc 10.xx.xx.xx 5000
HTTP/1.1 200 OK
Date: Fri, 17 May 2013 06:08:35 GMT
Server: Plack::Handler::Monoceros
Content-Type: text/plain
Transfer-Encoding: chunked

1e
OK OK OK OK OK OK OK OK OK OK 
0

HTTP/1.1 200 OK
Date: Fri, 17 May 2013 06:08:35 GMT
Server: Plack::Handler::Monoceros
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: close

1e
OK OK OK OK OK OK OK OK OK OK 
0
</code></pre>

<p>と、レスポンスも2つ得られました。Transfer-Encodingがchunkedにもなってますね。</p>

<h2>Monocerosをnginx のバックエンドに設置してのベンチマーク</h2>

<p>HTTP/1.1対応できたので、早速nginxのバックエンドにしてベンチマークしてみました。</p>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="monoceros-keepalive-bench.png" src="http://blog.nomadscafe.jp/2013/05/17/monoceros-keepalive-bench.png" width="551" height="308" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></span></p>

<p>Upstream KeepAliveを有効にすると、大体倍の性能になるようですね。ベンチマークに使ったのはab、参考としてabもkeepalive on/off切り替えてデータを取得しました。なお、直接 Monocerosにアクセスすると、2万強のreq/secがでます。この差がReverse Proxyする分のオーバーヘッドですね</p>

<p>ベンチマークに使った環境は前回と同じ、Xeon L5630 2.13GHz 4コア/8スレッド を2つ積んだサーバです。nginxとMonocerosを同じサーバに導入し、abは別のサーバから実行しました。</p>

<p>ベンチマークに使ったアプリケーション</p>

<pre><code>my $body = 'OK 'x10;
sub {
    [200, ['Content-Type'=&gt;'text/plain','Content-Length'=&gt;length($body)],[$body]]
}
</code></pre>

<p>残念ながら ab が Transfer-Encoding: chunked による KeepAliveに対応していないので、Content-Lengthを付けています。</p>

<p>Monocerosの起動は、</p>

<pre><code>$ carton exec -- plackup -E production --port 5000 --max-workers=10 -s Monoceros --max-reqs-per-child=50000 -a app.psgi
</code></pre>

<p>nginx.confは主なところを抜き出すと以下。</p>

<pre><code>worker_processes 1;
events {
    use epoll;
    worker_connections  10000;
}
http {
    keepalive_timeout      5 3;
    keepalive_requests     50000;
    upstream backend {
        server 127.0.0.1:5000;
        keepalive 100;
    }
    server {
        listen       8080;
        server_name  localhost;
        location / {
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            proxy_pass http://backend;
        }
    }
}
</code></pre>

<p>ab は -c 100 で実行しました。</p>

<pre><code>$ ab -k -c 100 -n 50000 'http://10.xx.xx.xx:8080/
</code></pre>

<p>リクエストの数や接続数次第ではnginxのworker_processes/keepaliveなどの設定を変更する必要がありそうです。</p>
]]>
        

    </content>
</entry>

<entry>
    <title>Monoceros というPrefork型だけどC10Kの接続を捌くことができるPSGI/Plackサーバ書きました</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/05/monoceros-preforkc10kpsgiplack.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.235</id>

    <published>2013-05-14T01:32:23Z</published>
    <updated>2013-05-14T01:51:56Z</updated>

    <summary>Monoceros というPSGI/Plackサーバ書きました https://...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>Monoceros というPSGI/Plackサーバ書きました</p>

<p><a href="https://metacpan.org/release/Monoceros">https://metacpan.org/release/Monoceros</a><br />
<a href="https://github.com/kazeburo/Monoceros">https://github.com/kazeburo/Monoceros</a></p>

<p>StarmanやStarletのようなPreforkなアプリケーションサーバでは、コネクションの維持イコールプロセスの占有なので、HTTPのKeepAliveは無効にするのが一般的ですが、負荷の高いサービスではTIME_WAIT状態のソケットが溜まったり、SYN-ACKの再送問題などあり、KeepAliveを使いたいという欲求があったりなかったりします。</p>

<p>Monoceros はリクエストを処理するworkerの他に、イベントドリブンで動くコネクション管理プロセスを立てて、クライアントからの接続ソケットをunix domain socketを使いプロセス間でやりとりします。待機中の接続をPreforkなworkerではなくイベントドリブンのプロセスで管理することで、プロセスを占有することなく大量のコネクションを捌く事ができます。適切に設定すれば10000接続もいけると思います。</p>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="monoceros1.png" src="http://blog.nomadscafe.jp/2013/05/14/monoceros1.png" width="552" height="309" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></span></p>

<p>コネクション管理プロセスでは<a href="https://metacpan.org/release/AnyEvent">AnyEvent</a>を使って接続をイベントドリブンで処理して、クライアントからの接続ののち最初のリクエストが読み取れる段階になったら<a href="https://metacpan.org/release/IO-FDPass">IO::FDPass</a>を使ってWorkerにソケットを受け渡します。Workerは受け取ったソケットからリクエストを読み取ってクライアントにレスポンスを直接返します。KeepAliveが有効であれば、その接続を維持して再度読み込み待ちにするようにコネクション管理プロセスに情報を送信し、Workerは次のリクエストに備えます。</p>

<p>実際にはDEFER_ACCEPTの処理やmax_keepalive_requestに達した際の処理があるのでもう少し複雑です。興味のある方はソースコードを参考にしてもらえたらと思います。</p>

<p>Workerは、高速で安定しているStarletを継承してほぼそのまま利用しています。なので現状HTTP/1.0のKeepAliveだけをサポートしています。</p>

<h2>ベンチマーク</h2>

<p>さっそくベンチマークです。Monocerosと<a href="https://metacpan.org/release/Starman">Starman</a>/<a href="https://metacpan.org/release/Starlet">Starlet</a>を比べてみます。</p>

<p>今回ベンチマークに使った各ソフトウェアのバージョンは以下になります</p>

<pre><code>Plack (1.0023)
EV (4.15)
Guard (1.022)
Starlet (0.18)
HTTP::Parser::XS (0.16)
Monoceros (0.08)
Starman (0.3011)
</code></pre>

<p>サーバは Xeon L5630 2.13GHz 4コア/8スレッド を2つ積んだサーバです</p>

<p>それぞれ以下のオプションで起動します。MonocerosとStarmanは1接続あたりのKeepAliveリクエスト数の上限設定がありません。</p>

<pre><code>$ carton exec -- plackup -E production --port 5000 --max-workers=15 -s Starlet --max-keepalive-reqs=50000 --max-reqs-per-child=50000 -a app.psgi
$ carton exec -- plackup -E production --port 5000 --max-workers=15 -s Monoceros --max-reqs-per-child=50000 -a app.psgi
$ carton exec -- starman --preload-app --workers=15 --max-requests=50000 -a app.psgi
</code></pre>

<p>app.psgiの中身は</p>

<pre><code>use Plack::Builder;
my $length = 12;
my $body = 'x'x$length;
builder {
    enable 'AccessLog', logger =&gt; sub { };
    sub {
        #select undef,undef,undef,0.01;
        my $env = shift;
        my $req = Plack::Request-&gt;new($env);
        my @params = $req-&gt;param('foo');   
        [200, ['Content-Type'=&gt;'text/plain','Content-Length'=&gt;length($body)],[$body]]
    }
};
</code></pre>

<p>になっています。</p>

<p>ベンチマークに使った ab のオプションは</p>

<pre><code>KeepAlive無効時 $ ab -c 100 -n 50110 http://../foo?foo=bar&amp;bar=baz&amp;baz=hoge&amp;hoge=foo
KeepAlive有効時 $ ab -k -c 100 -n 50110 http://../foo?foo=bar&amp;bar=baz&amp;baz=hoge&amp;hoge=foo
</code></pre>

<h2>ベンチマーク結果</h2>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="monoceros2.png" src="http://blog.nomadscafe.jp/2013/05/14/monoceros2.png" width="596" height="340" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></span></p>

<p>まず、MonocerosはKeepAlive無効時にStarletとほぼ同等の性能がでます。内部の動作でもselect(2)が一回多いだけでほぼ同じ動きをしています。</p>

<p>そしてKeepAliveを有効にした場合、StarmanとStarletが大きく数値を伸ばしているのに対して、Monocerosは少し増えただけのように思えます。</p>

<p>これにはいくつか理由があります。まずabは接続できたコネクションを優先して使ってリクエストを行います。-c 100と同時接続数を100に指定しても実際には100回の接続を均等に使いません。最初に接続できたコネクションを中心にリクエストを行ってしまいます。Starman/Starletでは &#8212;workers/&#8212;max-workers に指定された値、ここでは15接続に偏ってアクセスします。</p>

<p>Starman/Starletでは1つの接続が1つのプロセスを占有して処理されるのでKeepAlive有効時に高速になりますが、逆にMonocerosではAnyEventのプロセスとworker間のソケットのやりとりをして多くの接続を受け付け、接続とプロセスを分離して扱えるように設計されているので、その分多少のオーバーヘッドがあります。それでもKeepAlive有効時に高速になるようにkazuho氏のアドバイスの参考に様々な工夫をしています。</p>

<p>Monoceros雑感<br />
<a href="http://d.hatena.ne.jp/kazuhooku/20130425/1366851011">http://d.hatena.ne.jp/kazuhooku/20130425/1366851011</a></p>

<p>kazuho++</p>

<h2>KeepAlive Timeoutを考慮したベンチマーク</h2>

<p>ApacheBenchの動作は上で説明した通りなのですが、実際のブラウザ／Proxyとの通信では状況が異なります。KeepAliveを使って何回かのリクエストを行った後もまたその接続を使い回す可能性あるので、ブラウザ側かサーバ側でコネクションを切断するまで何もしない状態で接続が維持されます。Starman/Starletではそういった接続でも１つのプロセスを占有してしまうので、次のリクエストを受け付けることができなくなります。</p>

<p>Starman/Starletにある &#8212;keepalive-timeout というオプションはKeepAliveの次のアクセスを待つまでの時間で、通信のない接続にプロセスがいつまでも占有されてしまうのを防ぎます。ApacheではKeepAliveTimeoutですね。</p>

<p>以下のブログも参考になります。</p>

<p>keep-aliveでHTTPコネクションを放置するベンチマーク(abパッチ)<br />
<a href="http://mtl.recruit.co.jp/blog/2008/09/keepalivehttpab.html">http://mtl.recruit.co.jp/blog/2008/09/keepalivehttpab.html</a></p>

<p>上記のブログと同じようなベンチマークをperlのCoroとFurlを使って書いてみました。FurlはデフォルトでKeepAliveになります。</p>

<pre><code>use strict;
use warnings;
use 5.10.0;
use AnyEvent;
use Coro;
use FurlX::Coro::HTTP;
use Time::HiRes;
use Statistics::Basic qw(:all);
use List::Util qw/max min/;

my $reqs_per_thread = $ARGV[0];
my $threads = $ARGV[1];
my $url = $ARGV[2];

my $cv = AE::cv;
my @coros;
my @statistics;
my $errors = 0;
my %ua;
for my $id (1..$threads) {
    $cv-&gt;begin;
    push @coros, async {

        warn "[$id] start";
        $ua{$id}  = FurlX::Coro::HTTP-&gt;new(
            timeout =&gt; 5,
        );
        for (1..$reqs_per_thread) {
            my @t = Time::HiRes::gettimeofday;
            my @res = $ua{$id}-&gt;get($url);
            my $ela = Time::HiRes::tv_interval ( \@t );
            push @statistics, $ela;
            $errors++ if $res[1] != 200;
        }
        warn "[$id] end";
        $cv-&gt;end;
    };
}

my @t = Time::HiRes::gettimeofday;
$_-&gt;join for @coros;
$cv-&gt;recv;
my $total =  Time::HiRes::tv_interval ( \@t );

say sprintf 'finished in %s sec. concurrency: %s reqs_per_thread: %s', $total, $threads, $reqs_per_thread;
say sprintf 'requests: failed %s', $errors;
say '= per-reqs statistics =';
say sprintf 'minimum: %s sec', min(@statistics);
say sprintf 'maximum: %s sec', max(@statistics);
say sprintf 'average: %s sec', average(@statistics);
say sprintf 'median: %s sec', median(@statistics);
</code></pre>

<p>1接続に付き250リクエストを接続数100と1000でStarletとMonocerosに対して実行してみます</p>

<p>まずStarlet。</p>

<p>■接続数100</p>

<pre><code>finished in 15.043832 sec. concurrency: 100 reqs_per_thread: 250
requests: failed 80
= per-reqs statistics =
minimum: 0.000444 sec
maximum: 5.991323 sec 
average: 0.03 sec
median: 0 sec
</code></pre>

<p>■接続数1000</p>

<pre><code>finished in 177.534857 sec. concurrency: 1000 reqs_per_thread: 250
requests: failed 14765
= per-reqs statistics =
minimum: 0.00041 sec
maximum: 6.146582 sec
average: 0.37 sec
median: 0 sec
</code></pre>

<p>5秒以上リクエスト(接続)に時間が掛かかることもあり、何度もリクエストに失敗しています。</p>

<p>次にMonoceros。</p>

<p>■接続数100</p>

<pre><code>finished in 4.0479 sec. concurrency: 100 reqs_per_thread: 250
requests: failed 0
= per-reqs statistics =
minimum: 0.000625 sec
maximum: 0.045615 sec
average: 0.02 sec
median: 0.02 sec
</code></pre>

<p>■接続数1000</p>

<pre><code>finished in 43.784275 sec. concurrency: 1000 reqs_per_thread: 250
requests: failed 0
= per-reqs statistics =
minimum: 0.001168 sec
maximum: 0.402207 sec
average: 0.17 sec
median: 0.17 sec
</code></pre>

<p>全体の掛かる時間が短くなりました。リクエスト単位でも最大で0.4秒でレスポンスが得られ、リクエストが失敗するとはありません。Starletに比べて最短のレスポンス時間とレスポンス時間の中央値が悪くなっていますが、このあたりがMonocerosのオーバーヘッドなのでしょう。</p>

<h2>結論</h2>

<p>アプリケーションサーバでKeepAliveを有効にして大量の接続を維持し隊！ということがあればMonocerosは十分に使えると思われます。ぜひお試しください</p>
]]>
        

    </content>
</entry>

<entry>
    <title>Plack/PSGIアプリケーションのメモリリークをDevel::Leak::Objectでチェック</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/04/plackpsgidevelleakobject.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.234</id>

    <published>2013-04-18T08:39:47Z</published>
    <updated>2013-04-18T08:42:47Z</updated>

    <summary>dannさんが以前Catalystでやってたのを参考に、Plack/PSGIアプ...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>dannさんが<a href="https://dann.g.hatena.ne.jp/dann/20080922/p2">以前Catalystでやってた</a>のを参考に、Plack/PSGIアプリケーションのメモリリークを <a href="https://metacpan.org/module/Devel::Leak::Object">Devel::Leak::Object </a>で調べる方法</p>

<p>plackup を -MDevel::Leak::Object 付けて起動</p>

<pre><code>$ plackup -MDevel::Leak::Object=GLOBAL_bless -e '$Devel::Leak::Object::TRACKSOURCELINES = 1' -s Starlet --max-workers=1 app.psgi
</code></pre>

<p>-e &#8216;$Devel::Leak::Object::TRACKSOURCELINES = 1&#8217; を付けると行数まで出力してくれる</p>

<p>app.psgiはこんなの</p>

<pre><code>sub {
   my $env = shift;
   my $ref;$ref = bless \$ref, 'XXX';
   [200,['Content-Type'=&gt;'text/html'],["OK"]];
};
</code></pre>

<p>Starletの &#8212;max-reqs-per-child のデフォルトは 100 なので、ab使って100回アクセス。</p>

<pre><code>$ ab -n 100 http://localhost:5000/
</code></pre>

<p>すると、plackupを起動したコンソールの最後に、</p>

<pre><code>...
127.0.0.1 - - [18/Apr/2013:17:21:17 +0900] "GET / HTTP/1.0" 200 2 "-" "ApacheBench/2.3"
127.0.0.1 - - [18/Apr/2013:17:21:17 +0900] "GET / HTTP/1.0" 200 2 "-" "ApacheBench/2.3"
127.0.0.1 - - [18/Apr/2013:17:21:17 +0900] "GET / HTTP/1.0" 200 2 "-" "ApacheBench/2.3"
Tracked objects by class:
    Config                                   1
    Errno                                    1
    POSIX::SigRt                             1
    XXX                                      100

Sources of leaks:
Config
     1 from /path/to/perl5/perlbrew/perls/perl-5.16/lib/5.16.3/darwin-2level/Config.pm line: 73
Errno
     1 from /path/to/perl5/perlbrew/perls/perl-5.16/lib/5.16.3/darwin-2level/Errno.pm line: 167
POSIX::SigRt
     1 from /path/to/perl5/perlbrew/perls/perl-5.16/lib/5.16.3/Tie/Hash.pm line: 246
XXX
   100 from /path/to/Desktop/app.psgi line: 3
</code></pre>

<p>とでます。これで XXX があやちいことがわかりましたね</p>
]]>
        

    </content>
</entry>

<entry>
    <title>Plack::Middleware::AccessLog gets faster with Apache::LogFormat::Compiler </title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/04/plackmiddlewareaccesslog-gets-faster-with-apachelogformatcompiler.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.233</id>

    <published>2013-04-09T07:17:09Z</published>
    <updated>2013-04-09T07:28:35Z</updated>

    <summary>Plack-1.0023 から Plack::Middleware::Acces...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p><a href="https://metacpan.org/release/MIYAGAWA/Plack-1.0023">Plack-1.0023</a> から Plack::Middleware::AccessLog が <a href="Apache::LogFormat::Compiler">Apache::LogFormat::Compiler</a> を使うようになって、アクセスログの出力が速くなりました。</p>

<p>これまでの PM::AccessLog はリクエストの度に ログフォーマットの文字列を正規表現でパース、必要なメソッドを呼び出してアクセスログを生成していましたが、ALFCは初期化フェーズで ログフォーマットの文字列を1つのperlのコードに変換してしまうので、低コストでログを生成することができます。</p>

<p>以下ベンチマーク結果。アクセスログを有効にしただけのHello Worldだと、1.5倍くらい早くなる模様。memcachedに数回リクエストを送るだけのアプリケーションならプロダクション環境でも効果があるんじゃないでしょうか。ちなみに<a href="https://gist.github.com/kazeburo/5302308">サーバを介さないベンチマーク</a>だと6倍ぐらい高速になってました</p>

<h2>Benchmarks</h2>

<h3>HelloWorld.psgi</h3>

<pre><code>use Plack::Builder;
builder {
    enable 'AccessLog', logger =&gt; sub { };
    sub { [200, ['Content-Type'=&gt;'text/plain','Content-Length'=&gt;11],['Hello World']] }
};
</code></pre>

<h3>Setup</h3>

<p>Plack-1.0022</p>

<pre><code>$ cat cpanfile
requires 'Plack', '== 1.0022';
requires 'Starlet';
requires 'HTTP::Parser::XS';
$ carton install
$ carton exec -- -- plackup -s Starlet -E production --max-workers=10 --max-reqs-per-child=10000 -a HelloWorld.psgi
</code></pre>

<p>Plack-1.0023</p>

<pre><code>$ cat cpanfile
requires 'Plack', '== 1.0023';
requires 'Starlet';
requires 'HTTP::Parser::XS';
$ carton install
$ carton exec -- -- plackup -s Starlet -E production --max-workers=10 --max-reqs-per-child=10000 -a HelloWorld.psgi
</code></pre>

<h3>ApacheBench Options</h3>

<pre><code>$ ab -c 8 -n 50000 http://localhost:5000/
</code></pre>

<h3>Result</h3>

<p>Plack-1.0022</p>

<pre><code>Concurrency Level:      8
Time taken for tests:   4.201 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Total transferred:      7300146 bytes
HTML transferred:       550011 bytes
Requests per second:    11900.85 [#/sec] (mean)
Time per request:       0.672 [ms] (mean)
Time per request:       0.084 [ms] (mean, across all concurrent requests)
Transfer rate:          1696.84 [Kbytes/sec] received
</code></pre>

<p>Plack-1.0023</p>

<pre><code>Concurrency Level:      8
Time taken for tests:   2.864 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Total transferred:      7300146 bytes
HTML transferred:       550011 bytes
Requests per second:    17460.12 [#/sec] (mean)
Time per request:       0.458 [ms] (mean)
Time per request:       0.057 [ms] (mean, across all concurrent requests)
Transfer rate:          2489.48 [Kbytes/sec] received
</code></pre>

<p>これが出た事でAxsLogに「高速」というアドバンテージがなくなった。PODを書き直さないと！</p>
]]>
        

    </content>
</entry>

<entry>
    <title>Apache::LogFormat::Compiler version 0.05 supports custom format strings</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/03/apachelogformatcompiler-version-005-supports-custom-format-strings.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.232</id>

    <published>2013-03-22T07:49:47Z</published>
    <updated>2013-03-22T07:51:26Z</updated>

    <summary>Apache::LogFormat::Compiler のバージョン0.05で、...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>Apache::LogFormat::Compiler のバージョン0.05で、ログフォーマットの %D とか %{..}i の文字を追加できるようになりました。</p>

<p><a href="https://metacpan.org/release/KAZEBURO/Apache-LogFormat-Compiler-0.05/">https://metacpan.org/release/KAZEBURO/Apache-LogFormat-Compiler-0.05/</a></p>

<p>こちらのticketを実装しました<br />
<a href="https://github.com/kazeburo/Apache-LogFormat-Compiler/issues/1">https://github.com/kazeburo/Apache-LogFormat-Compiler/issues/1</a></p>

<p>使い方</p>

<pre><code>my $log_handler = Apache::LogFormat::Compiler-&gt;new(
    '%z %{HTTP_X_FORWARDED_FOR|REMOTE_ADDR}Z',
    char_handlers =&gt; +{
        'z' =&gt; sub {
            my ($env,$req) = @_;
            return $env-&gt;{HTTP_X_FORWARDED_FOR};
        }
    },
    block_handlers =&gt; +{
        'Z' =&gt; sub {
            my ($block,$env,$req) = @_;
            # block eq 'HTTP_X_FORWARDED_FOR|REMOTE_ADDR'
            my ($main, $alt) = split('\|', $args);
            return exists $env-&gt;{$main} ? $env-&gt;{$main} : $env-&gt;{$alt};
        }
    },
);
</code></pre>

<p>他にもPlackへの依存がなくなったり、いくつか修正が入ってます。</p>
]]>
        

    </content>
</entry>

<entry>
    <title>モニカジ#3 #monitoringcasual に参加してMHA for MySQLの管理ツールの話をしてきた</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/03/3-monitoringcasual-mha-for-mysql.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.231</id>

    <published>2013-03-09T13:34:33Z</published>
    <updated>2013-03-09T13:43:46Z</updated>

    <summary>監視をテーマに参加者全員がゆるふわに好き放題しゃべる Monitoring Ca...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>監視をテーマに参加者全員がゆるふわに好き放題しゃべる Monitoring Casual Talk に参加してきました</p>

<p>Zussar: <a href="http://www.zusaar.com/event/521056">http://www.zusaar.com/event/521056</a></p>

<p>発表した内容はこちら。監視の話なのかどうかは謎</p>

<p><iframe src="http://www.slideshare.net/slideshow/embed_code/17032678" width="427" height="356" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="http://www.slideshare.net/kazeburo/mha-for-mysql" title="MHA for MySQL の話" target="_blank">MHA for MySQL の話</a> </strong> from <strong><a href="http://www.slideshare.net/kazeburo" target="_blank">Masahiro Nagano</a></strong> </div></p>

<p><a href="https://code.google.com/p/mysql-master-ha/">MHA for MySQL</a>の基本構成と弊社で使っているMHA管理ツールの紹介です。masterha_managerの設定とプロセス管理、そしてオンラインでのマスター切り替えをWebUIから行えるの非常に便利なツールです。オープンソースになってないのに喋りました。すみません。</p>

<p>今回感じたのが、モニカジ参加者かいわいでの監視ツールが zabbix か munin + nagios に固まってきている点。zabbix勢はツールの使いこなしに悩んでいて、nagios勢は設定ファイルの自動生成あたりがホットな話題という感じでした。</p>

<p>弊社は cloudforecast + データセンターの監視サービス + 一部nagios の環境。サーバ管理ツールとの連携とかあまりしていないので、監視漏れは気をつけていないとでてくるし、そろそろツールの改良，自動化などを考えたいと思っているところ。</p>

<p>ちなみに nagiosの設定ファイルは <a href="https://metacpan.org/module/Data::Section::Simple">Data::Section::Simple</a> と <a href="http://xslate.org/">Xslate</a>で生成してる。</p>

<pre><code>#!/usr/bin/env perl

use strict;
use warnings;
use Text::Xslate;
use Data::Section::Simple qw(get_data_section);
use Log::Minimal;
$Log::Minimal::AUTODUMP=1;

my @nodes;
for my $l ( split /\n/, get_data_section('data') ) {
    chomp($l);
    my ($ip, $host, $notify) = split /\s+/, $l;
    push @nodes, {
        ipaddr =&gt; $ip,
        hostname =&gt; $host,
        is_master =&gt; ( $host =~ m!^dbm! ) ? 1 : 0,
        notify =&gt; $notify ? 1 : 0,
    };
}

my $tx = Text::Xslate-&gt;new();
print $tx-&gt;render_string(get_data_section('tmpl'), { nodes =&gt; \@nodes });

__DATA__
@@ data
10.xx.xx.74 dbm101.service
10.xx.xx.74 dbs101.service

@@ tmpl
: for $nodes -&gt; $node {
define host{
  use                     generic-host
  max_check_attempts      3
  host_name               &lt;: $node.hostname :&gt;
  alias                   &lt;: $node.hostname :&gt;
  address                 &lt;: $node.ipaddr :&gt;
  contact_groups          ldnsg
}

define service{
  use                             generic-service
  host_name                       &lt;: $node.hostname :&gt;
  service_description             Disk Usage
  max_check_attempts              3
  normal_check_interval           15
  retry_check_interval            15
  notification_options            u,c,r
  notifications_enabled           1
  check_command                   check_mysql5_disk
}
..
:}

define hostgroup{
  hostgroup_name  mysql5
  alias           mysql5
  members         &lt;: $nodes.map(-&gt; $a { $a.hostname }).join(", ") :&gt;
}
</code></pre>

<p>サーバが増えても1行足すだけなので、お気楽ですね。</p>
]]>
        

    </content>
</entry>

<entry>
    <title>Plack::Middleware::AxsLog でフォーマットが任意に変更できるようになったよ &amp; PM::AccessLogとのベンチマーク</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/03/plackmiddlewareaxslog-pmaccesslog.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.230</id>

    <published>2013-03-08T01:24:28Z</published>
    <updated>2013-03-08T01:28:03Z</updated>

    <summary>日本語でも書く Plack::Middleware::AxsLog バージョン ...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>日本語でも書く</p>

<p>Plack::Middleware::AxsLog バージョン 0.10 をリリースしました。</p>

<p>cpan: <a href="https://metacpan.org/release/Plack-Middleware-AxsLog">https://metacpan.org/release/Plack-Middleware-AxsLog</a></p>

<p>今までのバージョンでは、combined、common、ltsvの3つのログフォーマットしか使えなかったんだけど、新しいバージョンで任意のフォーマットが使えるようになりました。</p>

<p>AxsLogがPM::AccessLogに比べて速いのは、ログ文字列の生成を正規表現ではなく、単純な文字列連結でやっていたためで、その代わりにフォーマットを自由に変更できない制限がありました。</p>

<p>けど、このバージョンから <a href="https://metacpan.org/release/Apache-LogFormat-Compiler">Apache::LogFormat::Compiler</a> を使っているので、速度を犠牲にすることなくフォーマットの変更が可能となりましたぞよ。</p>

<p>使い方</p>

<pre><code>use Plack::Builder;

builder {
    enable 'AxsLog',
        format =&gt; '%h %l %u %t "%r" %&gt;s %b "%{Referer}i" "%{User-agent}i" %D',
        response_time =&gt; 1,
        error_only =&gt; 1,
    $app
};
</code></pre>

<p>ちなみにマイクロベンチマークの結果。AccessLogよりも5倍程度高速</p>

<pre><code>Benchmark: running axslog, axslog_format, error_only_axslog, log, nolog for at least 3 CPU seconds...
    axslog:  3 wallclock secs ( 3.15 usr +  0.01 sys =  3.16 CPU) @ 15955.70/s (n=50420)
axslog_format:  3 wallclock secs ( 3.19 usr +  0.01 sys =  3.20 CPU) @ 16036.25/s (n=51316)
error_only_axslog:  4 wallclock secs ( 3.14 usr +  0.01 sys =  3.15 CPU) @ 42440.32/s (n=133687)
       log:  3 wallclock secs ( 3.18 usr +  0.01 sys =  3.19 CPU) @ 3216.61/s (n=10261)
     nolog:  4 wallclock secs ( 3.13 usr +  0.00 sys =  3.13 CPU) @ 432826.20/s (n=1354746)
                      Rate    log axslog axslog_format error_only_axslog   nolog
log                 3217/s     --   -80%          -80%              -92%    -99%
axslog             15956/s   396%     --           -1%              -62%    -96%
axslog_format      16036/s   399%     1%            --              -62%    -96%
error_only_axslog  42440/s  1219%   166%          165%                --    -90%
nolog             432826/s 13356%  2613%         2599%              920%      --
</code></pre>

<p>scriptはこれ <a href="https://github.com/kazeburo/Plack-Middleware-AxsLog/blob/master/logbench.pl">https://github.com/kazeburo/Plack-Middleware-AxsLog/blob/master/logbench.pl</a></p>
]]>
        

    </content>
</entry>

<entry>
    <title>Released! Plack-Middleware-AxsLog v0.10. Added format option to specify the log format</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/03/released-plack-middleware-axslog-v010-added-format-option-to-specify-the-log-format.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.229</id>

    <published>2013-03-07T02:36:45Z</published>
    <updated>2013-03-08T01:30:50Z</updated>

    <summary>I released Plack::Middleware::AxsLog ver...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>I released Plack::Middleware::AxsLog version 0.10 at last week.</p>

<p>cpan: <a href="https://metacpan.org/release/Plack-Middleware-AxsLog">https://metacpan.org/release/Plack-Middleware-AxsLog</a></p>

<p>Plack-Middleware-AxsLog &lt; 0.10 supports only combined, common, ltsv log format. And cannot specify the log format freely.</p>

<p>PM::AxsLog simply joined strings to generate logline. So it&#8217;s faster than PM::AccessLog. But does&#8217;t have ability to modify the log format.</p>

<p>By using <a href="https://metacpan.org/release/Apache-LogFormat-Compiler">Apache::LogFormat::Compiler</a>, PM::AxsLog version 0.10 can modify log format without performance loss.</p>

<p>usage</p>

<pre><code>use Plack::Builder;

builder {
    enable 'AxsLog',
        format =&gt; '%h %l %u %t "%r" %&gt;s %b "%{Referer}i" "%{User-agent}i" %D',
        response_time =&gt; 1,
        error_only =&gt; 1,
    $app
};
</code></pre>

<p>result of micro-benchmarking</p>

<pre><code>Benchmark: running axslog, axslog_format, error_only_axslog, log, nolog for at least 3 CPU seconds...
    axslog:  3 wallclock secs ( 3.15 usr +  0.01 sys =  3.16 CPU) @ 15955.70/s (n=50420)
axslog_format:  3 wallclock secs ( 3.19 usr +  0.01 sys =  3.20 CPU) @ 16036.25/s (n=51316)
error_only_axslog:  4 wallclock secs ( 3.14 usr +  0.01 sys =  3.15 CPU) @ 42440.32/s (n=133687)
       log:  3 wallclock secs ( 3.18 usr +  0.01 sys =  3.19 CPU) @ 3216.61/s (n=10261)
     nolog:  4 wallclock secs ( 3.13 usr +  0.00 sys =  3.13 CPU) @ 432826.20/s (n=1354746)
                      Rate    log axslog axslog_format error_only_axslog   nolog
log                 3217/s     --   -80%          -80%              -92%    -99%
axslog             15956/s   396%     --           -1%              -62%    -96%
axslog_format      16036/s   399%     1%            --              -62%    -96%
error_only_axslog  42440/s  1219%   166%          165%                --    -90%
nolog             432826/s 13356%  2613%         2599%              920%      --
</code></pre>

<p>benchmark script is here: <a href="https://github.com/kazeburo/Plack-Middleware-AxsLog/blob/master/logbench.pl">https://github.com/kazeburo/Plack-Middleware-AxsLog/blob/master/logbench.pl</a></p>
]]>
        

    </content>
</entry>

<entry>
    <title>Apache::LogFormat::Compiler is now ON CPAN!</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/03/apachelogformatcompiler-is-now-on-cpan.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.228</id>

    <published>2013-03-01T08:04:17Z</published>
    <updated>2013-03-01T08:10:52Z</updated>

    <summary>I uploaded Apache::LogFormat::Compiler t...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>I uploaded Apache::LogFormat::Compiler to CPAN.</p>

<p>cpan: <a href="https://metacpan.org/release/Apache-LogFormat-Compiler">https://metacpan.org/release/Apache-LogFormat-Compiler</a><br />
github: <a href="https://github.com/kazeburo/Apache-LogFormat-Compiler">https://github.com/kazeburo/Apache-LogFormat-Compiler</a></p>

<p>Apache::LogFormat::Compiler compiles log_format line &#8216;combined&#8217;, &#8216;common&#8217; or &#8216;%h %l %u %t &#8220;%r&#8221; %>s %b&#8217; to perl-code.</p>

<p>It&#8217;s faster than Plack::Middleware::AccessLog&#8217;s way that parses log_format by regexp each request.</p>

<pre><code>my $log_handler = Apache::LogFormat::Compiler-&gt;new();
my $compile_log_app = builder {
    enable sub {
        my $app = shift;
        sub {
            my $env = shift;
            my $res = $app-&gt;();
            $env-&gt;{psgi.errors}-&gt;print($log_handler-&gt;log_line($env,$res,6,0));
        }
    };
    sub{ [ 200, [], [ "Hello "] ] };
};
</code></pre>

<p>This sample psgiapp is 8 times faster than the app that uses PM::AcccessLog.</p>

<p>I have plan to use Apache::LogFormat::Compiler in <a href="https://metacpan.org/release/Plack-Middleware-AxsLog">Plack::Middleware::AxsLog</a> for customize any log_format.</p>

<p>generated sample perl-code.</p>

<pre><code>% perl -Ilib -ML -E 'say Apache::LogFormat::Compiler-&gt;new()-&gt;{log_handler_code} '
sub {
        my ($env,$res,$length,$time) = @_;
        my @lt = localtime;
        my $t = sprintf '%02d/%s/%04d:%02d:%02d:%02d %s', $lt[3], $abbr[$lt[4]], $lt[5]+1900, 
          $lt[2], $lt[1], $lt[0], $tz;
        q!! . ($env-&gt;{REMOTE_ADDR} || '-')
      . q! ! . '-'
      . q! ! . ($env-&gt;{REMOTE_USER} || '-')
      . q! ! . "[" . $t . "]"
      . q! "! . _safe($env-&gt;{REQUEST_METHOD}) . " " . _safe($env-&gt;{REQUEST_URI}) .
                       " " . $env-&gt;{SERVER_PROTOCOL}
      . q!" ! . $res-&gt;[0]
      . q! ! . (defined $length ? $length : '-')
      . q! "! . _string($env-&gt;{"HTTP_" . uc('Referer')})
      . q!" "! . _string($env-&gt;{"HTTP_" . uc('User_agent')})
      . q!"!
    }
</code></pre>
]]>
        

    </content>
</entry>

<entry>
    <title>Module::Build::Pluggale::CAPNfile - Module::Build meets cpanfile</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/02/modulebuildpluggalecapnfile---modulebuild-meets-cpanfile.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.227</id>

    <published>2013-02-28T07:43:08Z</published>
    <updated>2013-02-28T07:48:55Z</updated>

    <summary>I just released Module::Build::Pluggable...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>I just released Module::Build::Pluggable::CPANfile to CPAN. </p>

<p>Module::Build::Pluggable::CPANfile is plugin for <a href="https://metacpan.org/module/Module::Build::Pluggable">Module::Build::Pluggable</a> to include dependencies from <a href="https://metacpan.org/module/cpanfile">cpanfile</a>. </p>

<p>cpan: <a href="https://metacpan.org/release/Module-Build-Pluggable-CPANfile">https://metacpan.org/release/Module-Build-Pluggable-CPANfile</a><br />
github: <a href="https://github.com/kazeburo/Module-Build-Pluggable-CPANfile">https://github.com/kazeburo/Module-Build-Pluggable-CPANfile</a></p>

<p>This is <a href="https://metacpan.org/module/Module::Install::CPANfile">Module::Install::CPANfile</a> for Module:Build.</p>

<p>Here is short sample cpanfile and Build.PL.</p>

<pre><code># cpanfile
requires 'Plack', 0.9;
on test =&gt; sub {
    requires 'Test::Warn';
};

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

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

<p>Prereqs will be dumped into (MY)?META.(yml|json)</p>

<p>Try it!</p>
]]>
        

    </content>
</entry>

<entry>
    <title>Module::Buildとcpanfileを組み合わせる案</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/02/modulebuildcpanfile.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.226</id>

    <published>2013-02-28T02:15:49Z</published>
    <updated>2013-02-28T07:17:51Z</updated>

    <summary> Module::Install::CPANfileと同じ事をModule::B...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p><a href="https://metacpan.org/module/Module::Install::CPANfile"> Module::Install::CPANfile</a>と同じ事をModule::Buildでもやりたい</p>

<p>Build.PL</p>

<pre><code>use Module::Build;
use Module::CPANfile;

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

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

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

    create_readme  =&gt; 1,
    create_license =&gt; 1,
);
$build-&gt;create_build_script();
</code></pre>

<p>cpanfile</p>

<pre><code>requires 'LWP::UserAgent' =&gt; '6.02';
requires 'HTTP::Message'  =&gt; '6.04'; 

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

on 'configure' =&gt; sub {
   requires 'Module::Build'     =&gt; '0.38';
   requires 'Module::CPANfile' =&gt; '0.9010';
};
</code></pre>

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

<h2>追記</h2>

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

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

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

    </content>
</entry>

<entry>
    <title>derivedがプラグインアーキテクチャになってGrowthForecastに直接POSTできるようになったので、Javaのヒープのモニタリングをしてみた</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/02/derivedgrowthforecastpostjava.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.225</id>

    <published>2013-02-19T03:33:48Z</published>
    <updated>2013-02-20T05:39:14Z</updated>

    <summary>「数字を出力するコマンドを定期的に実行して、秒間の変化量を memcachedプ...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>「<a href="http://blog.nomadscafe.jp/2013/02/-memcached.html">数字を出力するコマンドを定期的に実行して、秒間の変化量を memcachedプロトコルで取れるサーバを書いた</a>」で紹介した、<a href="https://metacpan.org/release/App-derived">derived</a>。memcahcedプロトコルで外からアクセスするだけじゃなくて、<a href="http://kazeburo.github.com/GrowthForecast/">GrowthForecast</a>に直接ポストできたら便利だなと思ったので、構成見直してプラグインでデータの出力方法をカスタマイズできるようにした。</p>

<p>CPAN: <a href="https://metacpan.org/release/App-derived">https://metacpan.org/release/App-derived</a> <br />
github: <a href="https://github.com/kazeburo/App-derived">https://github.com/kazeburo/App-derived</a></p>

<p>今まで</p>

<pre><code>$ derived -i 10 --port 12306 cmdsfile
</code></pre>

<p>だったのが、</p>

<pre><code>$ derived -i 10 -MMemcahced,port=12306 cmdsfile
</code></pre>

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

<p>GrowthForecastは</p>

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

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

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

<pre><code>$ derived -i 1 -MDumper,interval=1 cmdsfile
</code></pre>

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

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

<pre><code>$ 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
</code></pre>

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

<p>cmdsfileには</p>

<pre><code>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}'
</code></pre>

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

<p>そしてderivedを起動</p>

<pre><code>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
</code></pre>

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

<p>更新間隔はGrowthForecastなので1分で十分です。</p>

<p>そして出来たのがこんなグラフ。</p>

<p><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="derived2gf.png" src="http://blog.nomadscafe.jp/2013/02/19/derived2gf.png" width="488" height="573" class="mt-image-center" style="text-align: center; display: block; margin: 0 auto 20px;" /></span></p>

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

<h3>参考文献</h3>

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

<p><a href="http://d.hatena.ne.jp/learn/20090218/p1">Javaメモリ、GCチューニングとそれにまつわるトラブル対応手順まとめ - 日記のような何か</a></p>

<p><a href="http://www.atmarkit.co.jp/fjava/rensai3/javavm02/javavm02_2.html">＠IT：チューニングのためのJava VM講座（後編）</a></p>
]]>
        

    </content>
</entry>

<entry>
    <title>Nagios と derived でMySQLのスロークエリの量を監視する</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/02/nagios-derived-mysql.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.224</id>

    <published>2013-02-14T07:42:07Z</published>
    <updated>2013-02-14T07:51:57Z</updated>

    <summary>新しい機能をリリースした際に、MySQLに対して効率的ではないクエリが発行されて...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>新しい機能をリリースした際に、MySQLに対して効率的ではないクエリが発行されてしまって、それが積もってサービス全体に影響が出てしまう前に発見してアラートをあげたい。</p>

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

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

<pre><code>mysql&gt; show  status like 'Slow_queries';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries  | 7238  |
+---------------+-------+
1 row in set (0.00 sec)
</code></pre>

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

<p>Nagiosでこのようなカウンター系の数字を監視をするには、プラグインの中で取得したデータを別の場所に保存し、前回のデータと比べて差分を出す必要があります。既成のNagiosプラグインで差分が取れるのは、<a href="http://www.percona.com/doc/percona-monitoring-plugins/nagios/index.html">Percona Monitoring Plugins</a>に含まれる <a href="http://www.percona.com/doc/percona-monitoring-plugins/nagios/pmp-check-mysql-status.html">pmp-check-mysql-status</a> などがあります。</p>

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

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

<h2>derived</h2>

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

<p>CPAN: <a href="https://metacpan.org/release/App-derived">https://metacpan.org/release/App-derived</a><br />
github: <a href="https://github.com/kazeburo/App-derived">https://github.com/kazeburo/App-derived</a></p>

<p>インストールは cpanm で</p>

<pre><code>$ cpanm -n App::derived
</code></pre>

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

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

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

<p>起動</p>

<pre><code>$ derived --port 12306 -i 10 /path/to/cmdfile
</code></pre>

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

<p>telnet で確認</p>

<pre><code>$ 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
</code></pre>

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

<h2>check_memcached_val.pl</h2>

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

<p>github: <a href="https://github.com/kazeburo/check_memcached_val">https://github.com/kazeburo/check<em>memcached</em>val</a></p>

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

<pre><code>$ curl https://raw.github.com/kazeburo/check_memcached_val/master/check_memcached_val.pl &gt; check_memcached_val.pl
$ chmod +x check_memcached_val.pl
$ cp check_memcached_val.pl /path/to/nagios/libexec
</code></pre>

<p>基本的な使い方は</p>

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

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

<pre><code>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
}
</code></pre>

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

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

<h2>まとめ</h2>

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

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

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

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

<div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774145823/nomadscafejp-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://ecx.images-amazon.com/images/I/51H7Wq1BVDL._SL160_.jpg" alt="Nagios統合監視[実践]リファレンス (Software Design ｐlus)" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774145823/nomadscafejp-22/ref=nosim/" name="amazletlink" target="_blank">Nagios統合監視[実践]リファレンス (Software Design ｐlus)</a><div class="amazlet-powered-date" style="font-size:80%;margin-top:5px;line-height:120%">posted with <a href="http://www.amazlet.com/" title="amazlet" target="_blank">amazlet</a> at 13.02.14</div></div><div class="amazlet-detail">株式会社エクストランス 佐藤 省吾 Team-Nagios <br />技術評論社 <br />売り上げランキング: 237,444<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774145823/nomadscafejp-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div>
]]>
        

    </content>
</entry>

<entry>
    <title>memcached上の任意の値を監視できるnagiosプラグイン書いた</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/02/memcachednagios.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.223</id>

    <published>2013-02-13T01:39:50Z</published>
    <updated>2013-02-13T01:50:45Z</updated>

    <summary>指定したコマンドを定期的に実行してmemcachedプロトコルで結果が取得できる...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p><a href="http://blog.nomadscafe.jp/2013/02/-memcached.html">指定したコマンドを定期的に実行してmemcachedプロトコルで結果が取得できるサーバ - App::derived</a>とも組み合わせて使えるように、memcachedのキャッシュのキーを指定して、その値を監視できるNagiosプラグインを書きました。<a href="http://blog.nomadscafe.jp/2013/02/nagioscheck-snmp---rate.html">check_snmpと同様</a>に変化・差分量が計算できる機能もついてます。</p>

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

<p>check_memcached_val.pl<br />
<a href="https://github.com/kazeburo/check_memcached_val">https://github.com/kazeburo/check_memcached_val</a></p>

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

<h2>使い方</h2>

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

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

<p>正規表現での監視</p>

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

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

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

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

<h2>オプション</h2>

<dl>
<dt>-h, &#8212;help</dt>
<dd>ヘルプの表示</dd>

<dt>-H, &#8212;hostname=STRING</dt>
<dd>ホスト名・IPアドレス</dd>

<dt>-P, &#8212;port=INTEGER</dt>
<dd>ポート番号・デフォルトは「11211」</dd>

<dt>-k, &#8212;key=STRING</dt>
<dd>取得するキャッシュのキー</dd>

<dt>-s, &#8212;string=STRING</dt>
<dd>文字列一致の監視</dd>

<dt>-r, &#8212;ereg=REGEX</dt>
<dd>正規表現での監視。perlの正規表現が使えます</dd>

<dt>-R, &#8212;eregi=REGEX</dt>
<dd>正規表現での監視。大文字小文字無視</dd>

<dt>&#8212;invert-search</dt>
<dd>-s,-r,-Rの監視結果を反転します</dd>

<dt>-w, &#8212;warning=THRESHOLD</dt>
<dd>取得した値が数値の場合の監視閾値</dd>

<dt>-c, &#8212;critical=THRESHOLD</dt>
<dd>取得した値が数値の場合の監視閾値</dd>

<dt>-t, &#8212;timeout=INTEGER</dt>
<dd>memcachedサーバへ接続する際のタイムアウト。デフォルト「10」</dd>

<dt>&#8212;rate</dt>
<dd>変化・差分量監視を有効にする</dd>

<dt>&#8212;rate-multiplier=INTEGER</dt>
<dd>&#8212;rateで得られた数値に一定の係数を掛ける</dd>

</dl>

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

<h2>特別な値「0E0」</h2>

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

    </content>
</entry>

<entry>
    <title>Nagiosのcheck_snmpプラグインの --rate オプションを試す</title>
    <link rel="alternate" type="text/html" href="http://blog.nomadscafe.jp/2013/02/nagioscheck-snmp---rate.html" />
    <id>tag:blog.nomadscafe.jp,2013://1.222</id>

    <published>2013-02-09T14:27:58Z</published>
    <updated>2013-02-09T14:42:33Z</updated>

    <summary>Nagiosのプラグインで、データ転送速度など、変化量・差分を監視するには、プラ...</summary>
    <author>
        <name>Masahiro Nagano</name>
        <uri>http://blog.nomadscafe.jp/</uri>
    </author>
    
    
    <content type="html" xml:lang="ja" xml:base="http://blog.nomadscafe.jp/">
        <![CDATA[<p>Nagiosのプラグインで、データ転送速度など、変化量・差分を監視するには、プラグインでどこかファイルに監視結果を保存しておいて比較するという手が使えると<a href="http://blog.nomadscafe.jp/2013/02/-memcached.html">2つ前のエントリ</a>書きましたが、 check_snmpでもそれがサポートされていたので試してみました。恥ずかしながら知りませんでした。</p>

<p>check_snmpの「&#8212;rate」というオプションがそれ。</p>

<pre><code> --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.
</code></pre>

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

<pre><code>$ ./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
</code></pre>

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

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

<pre><code>$ ./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
</code></pre>

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

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

<pre><code>$ ./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 &amp;&amp; ./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
</code></pre>

<p>WARNINGになりました。</p>

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

<p>普通に便利ですね。</p>

<div class="amazlet-box" style="margin-bottom:0px;"><div class="amazlet-image" style="float:left;margin:0px 12px 1px 0px;"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774145823/nomadscafejp-22/ref=nosim/" name="amazletlink" target="_blank"><img src="http://ecx.images-amazon.com/images/I/51H7Wq1BVDL._SL160_.jpg" alt="Nagios統合監視[実践]リファレンス (Software Design ｐlus)" style="border: none;" /></a></div><div class="amazlet-info" style="line-height:120%; margin-bottom: 10px"><div class="amazlet-name" style="margin-bottom:10px;line-height:120%"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774145823/nomadscafejp-22/ref=nosim/" name="amazletlink" target="_blank">Nagios統合監視[実践]リファレンス (Software Design ｐlus)</a><div class="amazlet-powered-date" style="font-size:80%;margin-top:5px;line-height:120%">posted with <a href="http://www.amazlet.com/" title="amazlet" target="_blank">amazlet</a> at 13.02.09</div></div><div class="amazlet-detail">株式会社エクストランス 佐藤 省吾 Team-Nagios <br />技術評論社 <br />売り上げランキング: 236,015<br /></div><div class="amazlet-sub-info" style="float: left;"><div class="amazlet-link" style="margin-top: 5px"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4774145823/nomadscafejp-22/ref=nosim/" name="amazletlink" target="_blank">Amazon.co.jpで詳細を見る</a></div></div></div><div class="amazlet-footer" style="clear: left"></div></div>

<p>↑リファレンス的に使える良本です</p>
]]>
        

    </content>
</entry>

</feed>
