PSGIミドルウェア
PSGIミドルウェアとは
PSGIアプリケーションとPSGIサーバの間にあり、Plack::Middlewareという名前空間のもとに実装され、
Plack::Builderモジュールを利用すると、
use Plack::Builder;
my $app = sub {…};
$app = builder {
enable 'Plack::Middleware::Lint';
enable 'Plack::Middleware::StackTrace';
$app;
};Plack::Builderが提供するbuilderブロックの中で、
以降ではプロダクション環境で使用されるPlack::Middlewareをいくつか紹介します。
Static ── 静的コンテンツの配信
Plack::Middleware::StaticはCSSやJavaScriptなどの静的コンテンツの配信を行うミドルウェアです。Plackのディストリビューションに含まれています。
builder {
enable 'Static',
path => qr!^/(css|js|img)/!,
root => '/path/to/public';
$app;
}上記のような設定を行うと、
ただし、
AccessLog ── アクセスログの表示
AccessLogはその名のとおりアクセスログを出力するためのミドルウェアです。
builder {
enable "AccessLog",
format => "combined";
$app;
};ログのフォーマットにはcombined、common、
AccessLogミドルウェアでApacheでサポートされる、%D
enable "AccessLog::Timed",
format => '%h %l %u %t "%r" %>s %b %D';ログの出力先はデフォルトでは$env->{psgi.が示すファイルハンドルになります。多くの場合は標準エラーです。ファイルに書き出すときはloggerオプションにコードリファレンスを渡します。
use File::RotateLogs;
my $logger = File::RotateLogs->new();
builder {
enable "AccessLog",
format => 'combined',
logger => sub { $logger->print(@_) }
$app;
}File::RotateLogsはログファイルを指定した時間ごとに分割し、
ReverseProxy── アクセス元IPアドレスの取得
PSGIアプリケーションをApacheやnginxなどのリバースプロキシのもとで動作させた場合、$env->{REMOTE_はリバースプロキシのIPアドレスとなります。同じホスト上にリバースプロキシがあれば127.が格納されます。本来のクライアントのIPアドレスはどうなるかというと、X-Forwarded-Forというヘッダの末尾に追加されて送られてきます。X-Forwarded-ForヘッダはRFCなどで定義されているヘッダではありませんが、
PSGIアプリケーションでは次のようなコードで、X-Forwarded-Forから取得できます。
my $xff = $env->{HTTP_X_FORWARDED_FOR};
my ($ip) = $xff =~ /([^,\s]+)$/;Plack::Middleware::ReverseProxyはX-Forwarded-ForからIPアドレスを取得し、$env->{REMOTE_の上書きのほか、
use Plack::Builder;
builder {
enable_if { $_[0]->{REMOTE\_ADDR} eq '127.0.0.1' }
'ReverseProxy';
$app;
};
X-Forwarded-Forヘッダはクライアント側で簡単に詐称できてしまうので、X-Forwarded-Forを信用するようにしてください。
ServerStatus::Lite── サーバ状態の可視化
Starman、
ServerStatus::Liteにはいくつかオプションがあります。
builder {
enable "Lite",
path => '/server-status',
allow => ['127.0.0.1','192.168.0.0/16'],
counter_file => '/path/to/counter_file',
scoreboard => '/path/to/scoreboard';
$app;
};pathにサーバの状態を表示するためのURIを指定し、allowにそのURIに対してアクセス許可されるIPアドレスを指定します。もしIPアドレスが指定されていない場合、counter_はアクセス数と総転送量を記録するためのファイルです。そしてscoreboardにワーカプロセスの状態を記録するためのディレクトリを指定します。
HTTPクライアントでpathに指定したURIにアクセスすると、
$ curl http://localhost:5000/server-status Uptime: 1381942535 (23 seconds) Total Accesses: 3 Total Kbytes: 0 BusyWorkers: 1 IdleWorkers: 9 -- pid status remote_addr host method uri protocol ss 80060 _ 127.0.0.1 localhost:5000 GET / HTTP/1.1 14 80061 _ 127.0.0.1 localhost:5000 GET / HTTP/1.1 13 80062 _ 127.0.0.1 localhost:5000 GET / HTTP/1.1 13 80063 A 127.0.0.1 localhost:5000 GET /server-status HTTP/1.1 0 80064 .
図5は、
PSGIアプリケーションのホットデプロイ
Server::Starterを使ったホットデプロイ
Server::Starterは、
Server::Starter経由でPSGIサーバを起動するにはstart_コマンドを使います。
$ start_server --port 5000 -- plackup \
-s Starlet -a hello_world.psgi上記のコマンドを実行すると、exec(2)します。このときに、
子プロセスのStarletは環境変数からファイルディスクリプタを読み出し、
アプリケーションへの機能追加などでPSGIサーバの再起動が必要になった際には、
$ kill -HUP {pid}Server::StarterはHUPシグナルを受け取ると、
Server::Starterはこのように再起動処理を行うことで、
現場で使われるPSGIアプリケーションの起動方法
Server::StarterとPSGI/start_を次のようにして起動します。
$ start_server --port 5000 \
--signal-on-hup=USR1 \
-- /path/to/run_app.shポイントは2つあります。1つ目はstart_コマンドにplackupコマンドを直接渡さずにシェルスクリプトを使っている点、start_の--signalon-hupオプションとStarletの--spawn-intervalオプションです。
Webサービスの運用中にServer::Starter経由で起動しているStarletのワーカ数を変更をしたいと思っても、--maxworkersが書かれていると、start_を起動すると、
#!/bin/bash
exec plackup -s Starlet \
--spawn-interval 0.1 --max-worker 30 \
--max-reqs-per-child 1000 --min-reqs-per-child 500 \
-a /path/to/app.psgi
大量のアクセスを受けているサービスでは、fork(2)による負荷が問題となるケースがあります。Starletの起動オプションに--spawn-interval=秒数を追加すると、fork(2)する際に指定した秒数だけ間隔を開けます。また、--spawn-intervalの秒数を空けてワーカプロセスを順次終了させます。
start_に追加した--signal-on-hup=USR1はこのStarletの機能を活用するためのオプションで、
まとめ
本稿では、
さて、
