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);
どうでしょうか?