2013年6月アーカイブ

WEB+DB PRESS総集編[Vol.1~72]の書き下ろし特集「今,Web開発者に必要な知識」に記事を書かせて頂きました。内容はインフラですが、ハードウェアの話はあまりせず、クラウドやChefなどソフトウェアによってインフラを制御する話題を中心に書きました。

WEB+DB PRESS総集編[Vol.1~72]:書籍案内|技術評論社
http://gihyo.jp/book/2013/978-4-7741-5783-2

特集の目次は次のようになってます

これからエンジニアとして働く人へ 今,Web開発者に必要な知識

第1章:Webアプリケーションの全体像──それぞれの役割をきちんと理解しよう……伊藤直也
第2章:プログラミング編──サーバサイドとクライアントサイド……外村和仁
第3章:インフラ編──クラウド,監視,メトリクス…………長野雅広
第4章:データベース編──RDBMSとNoSQLの使い分け,データ分析……松信嘉範
第5章:テスト編──ユニットテストからはじめよう……渡辺修司

偉大なエンジニアに挟まれてdkdkです。

WEB+DB PRESSは小山さんの記事が載っているVol.1から読んでます。総集編の目次を見ながら当時何をやっていた振り返るのもいいのではないでしょうか。今も貴重な情報があるはずです。

WEB+DB PRESS 総集編〔Vol.1~72〕 (WEB+DB PRESS plus)
伊藤 直也 外村 和仁 長野 雅広 松信 嘉範 渡辺 修司
技術評論社
売り上げランキング: 172

I released AnyEvent::DNS::Cache::Simple to cpan today.

cpan: https://metacpan.org/release/AnyEvent-DNS-Cache-Simple
github: https://github.com/kazeburo/AnyEvent-DNS-Cache-Simple

This module will cache DNS responses of AnyEvent::DNS. And you can improve the performance of AnyEvent applications.

synopsis

use AnyEvent::HTTP;
use AnyEvent::DNS::Cache::Simple;
use Cache::LRU::WithExpires;

my $cache = Cache::LRU::WithExpires->new;
my $guard = AnyEvent::DNS::Cache::Simple->register(
    ttl => 60,
    negative_ttl => 5,
    timeout => [1,1],
    cache => $cache # default uses Cache::Memory::Simple
);
for my $i ( 1..3 ) {
    my $cv = AE::cv;
    http_get "http://mixi.jp/", sub {
        say "get $i";
        $cv->send;
    };
    $cv->recv;
}

undef $guard;

# not using cahce
my $cv = AE::cv;
http_get "http://mixi.jp/", sub {
    warn "get 4";
    $cv->send;
};
$cv->recv;

AnyEvent::DNS::Cache::Simple というモジュールをリリースしました。AnyEvent::DNSの問い合わせ結果をキャッシュしてAnyEventを利用したアプリケーションのパフォーマンスを向上させることができます。

PSGI/Plack/PSGIアプリケーションを動かす時に一番使われているのは plackup でしょう。

$ cat app.psgi
use Plack::Builder;
use MyApp;
my $app = MyApp->psgi_app;
builder {
    enable 'ServerStatus::Lite', => ..;
    $app;
};
$ plackup -E production -s Starlet --max-workers 30 --port 5000 -a app.psgi

plackup コマンドの -s にハンドラ名を指定して起動します。本番環境では -E や $ENV{PLACK_ENV} を指定してStackTraceやLintといった開発に便利なPlack::Middlewareが追加されないようにする必要がありますね。

Starmanの場合は、starman コマンドも用意されていて

$ starman  --workers 30 --port 5000 --preload-app --disable-keepalive app.psgi

のようにしていると思います。starmanコマンドの場合はデフォルトで-E(env)が deployment となります。

plackupやstarmanコマンドが利用しているのが Plack::Runner です。

my $runner = Plack::Runner->new();
$runner->parse_options(qw/-E production -s Starlet --max-workers 30 --port 5000 -a app.psgi/);
$runner->run();

Plack::Runnerが plackup コマンドの実装そのもので、渡されたオプションの解析と、PSGIアプリケーションの読み込み方法(RestarterやShotgunなどのPlack::Loader)の設定などを行っています。

Plack::LoaderはPlack::Runnerから呼び出されますが、別のモジュール内やスクリプトからPlackのサーバを起動したい場合などに直接使う事もできます。Plack::Loaderにはpsgiファイルの読み込み機能がないので、自前で用意する必要があります。また、PLACK_ENVを自動で設定することもありません。

my $app = MyApp->psgi_app;
$app = builder {
    enable 'ServerStatus::Lite', => ..;
    $app;
};

my $handler = Plack::Loader->load(
    'Starlet',
    max_worker => 30
    port => 5000
);
$handler->run($app);

と、ここまでくると、直接 Plack::Handler を使う事もでき、

my $handler = Plack::Handler::Starlet->new(
    max_worker => 30
    port => 5000
);
$handler->run($app);

とも書けます。

本番環境でのあれこれ

本番環境でも plackup が使われていることが多いのですが、2つほど困る事があります。1つ目はPLACK_ENVの指定し忘れで開発用のStackTraceなどが読み込まれてしまう事故。もうひとつがServer::Starterと組み合わせたときに起動オプションの変更が kill -HUP では出来ないことです

無停止でのアプリケーションの再起動を行うためにServer::Starter(start_server)を使っている思いますが、例えば、daemontoolsのrunファイルに

export PATH=...
exec start_server --port 5000 --signal-on-hup=USR1 \
      -- plackup -E production -s Starlet --spawn-interval=1 --max-worker=5 -a /path/to/app.psgi 2>&1

と書いて実行していて、「いっけねー、ワーカー少なすぎたわー」となった時に、runファイルを書き換えて、svc -h してもワーカーの数は変わってくれません。svc -t などで一度Server::Starterごと停止して、新しいオプションでServer::Starterを起動しなおす必要があります。これではせっかく無停止でのアプリケーションの再起動ができるのに残念です。

そこでおすすめなのが、plackup をシェルスクリプトなどでラップする方法と、plackup を使わない方法です

シェルスクリプトの場合、

$ cat app.sh
DIR=$(cd $(dirname $0) && pwd)
exec plackup -E production -s Starlet --spawn-interval=1 --max-worker=5 -a $DIR/app.psgi

$ cat /service/myapp/run
...
exec start_server --port 5000 --signal-on-hup=USR1 -- /path/to/app.sh 2>&1

となります。この方法だと app.sh を書き換えて svc -h すると app.shが必ず再実行されるのでオプションの変更も適用されるはずです。この方法はmala氏に教えてもらいました。

もうひとつが plackup を使わない方法。上で紹介したPlack::Runnerなどのモジュールを使い、本番環境用起動コマンドを書くというもの。できるだけPLACK_ENVの指定し忘れ事故を防ぐ構成になっていると良いんじゃないかなぁと思います。

例えば、Plack::Loaderを使うようにすると、PLACK_ENVの有る無しによって動作が変わってしまうことはなくなります

$ cat produnction_server.pl
#!/usr/bin/env perl

use strict;
use warnings;
use FindBin;
use Plack::Util;
use Plack::Loader;
use Plack::Builder;

my $app = Plack::Util::load_psgi $FindBin:Bin . '/app.psgi';
$app = builder {
    enable "ServerStatus::Lite", ...;
    $app;
};
Plack::Loader->load(
    'Starlet'
    'max_workers' => 30,
    'port' => 5000
)->run($app);

どうでしょうか?