Plack──PSGIユーティリティ
PSGIのおかげで、フレームワークはCGIやFastCGI、mod_perlといった環境の差異を吸収するためのコードを書く必要はなくなり、PSGIのインタフェースだけを実行すればよくなりました。実際に多くのフレームワークがすでにPSGIをサポート していますが、さて、そうしたアプリケーションをどうやって動かせばよいのでしょうか。
Apacheなど既存のWebサーバでPSGIアプリケーションを動かすには、CGI、FastCGI、mod_perlなどのインタフェースをPSGIに変換する必要があります。また、PSGIをネイティブで実行できるPerlベースのHTTPサーバもほしいところです。
Plackはそうした要件を満たすためのユーティリティで、リファレンス実装としてのPSGIサーバやそれらへのアダプタ、周辺ライブラリや後述するミドルウェアが含まれています。
plackupコマンド
Plackには非常に高速かつ簡単に起動できるスタンドアロンのPSGIサーバ(HTTP::Server::PSGI)とコマンドラインツールが付属しています。
スタンドアロンPSGIサーバ
CPANからPlackをインストールしたあと、リスト1で記述したHello Worldアプリケーションをhello.psgiというファイルにリネームし、plackupというコマンドを使用して起動してみましょう。
> plackup hello.psgi
HTTP::Server::PSGI: Accepting connections at
http://0:5000/
先ほど記述したアプリケーションがスタンドアロンアプリケーションとして起動しました。ブラウザでhttp://localhost:5000/
を開くと「Hello World」と表示されたはずです。
同様にFastCGIでこのアプリケーションを起動するには、
> plackup -s FCGI --listen /tmp/fastcgi.sock hello.psgi
のようにします。こうすると、/tmp/fastcgi.sockをUNIXドメインソケットとしてFastCGIデーモンが起動します。あとはApache、lighttpd、nginxなどでFastCGIに接続するための設定を行えばよいでしょう。--portオプションを利用することでTCP/IPで通信することもできます。
なお、CGIやmod_perlについてはスタンドアロンで起動するのではなく、Webサーバから呼び出される形になるためplackupは利用できませんが、.psgiのパスをWebサーバから設定したり、.cgiファイルから呼び出す、といった形で簡単に再利用できます。
スタックトレースとアクセスログ
plackupにはアプリケーションがエラーを起こしたらそのスタックトレースを表示する機能が含まれています。試しに、
> plackup -e 'sub { die "Foo" }'
としてみましょう。-e
オプションはperlと同様、指定されたコードをevalするオプションです。これを実行してブラウザを開くと図3 のようなHTMLが表示されます。エラーの起こった個所を、実際のコードを見ながら順に追えて非常に便利です。
図3 スタックトレースの表示
また、Apacheと同様の形式のアクセスログがターミナルに表示されます。これも開発時にデフォルトで有効になっている機能です。
スタックトレースとアクセスログを無効にするには、-E
オプションでdevelopment以外の値を指定します。
> plackup -E production app.psgi
そのほかのオプション
そのほかにも、アプリケーションが変更されたら自動で再起動する-r
オプションなど、便利なオプションが用意されています。詳しくはplackup--help
と実行してください。
Plack::App──便利なアプリケーション群
Plackに付属しているツールでちょっと便利なのが、Plack::Appに含まれているアプリケーション群です。スタティックファイルの配信、既存CGIスクリプトをPSGI化して実行、といったアプリケーションが含まれていて、plackupツールなどから簡単に利用できます。たとえば、
> plackup -MPlack::App::Directory \
-e 'Plack::App::Directory->new->to_app'
のようにすると、カレントディレクトリをドキュメントルートとしたスタティックなWebサーバが起動します。
Plackをインストールするだけで、ほかのWebサーバをインストールすることなく利用可能なので、これだけでも開発環境などで便利に利用できます。また、こうしたPlack::Appに含まれるモジュールは、ユーザが開発するアプリケーションに組み込むことも容易にできるようになっています。
PSGIミドルウェア
PSGIアプリケーションはコードリファレンスであり、サーバはHTTPリクエストを$envの形に変換してコードに渡し、アプリケーションは受け取ったハッシュ($env)を利用してリクエストを処理し、レスポンスを配列リファレンスにして返します。このようなシンプルな仕様であることのメリットには、実装が簡単であり、特定のモジュールに依存しないということのほかに、実行時にアプリケーションや環境、レスポンスを(外側から)動的に書き換えるためのプロキシ的なコードを差し込むことが可能である、ということが挙げられます。このしくみを「PSGIミドルウェア」と呼び、図示すると図4 のようになります。
図4 PSGIミドルウェア
リスト2 はHTTPクライアントのIPアドレスをREMOTE_ADDRから取得して表示するシンプルなアプリケーションですが、これをリスト3 のようにしてみるとどうでしょう。$appは先ほどと同様のコードですが、$mwでは$envを受け取り、そのREMOTE_ADDRをランダムに2分の1の確率で1.2.3.4に変更して、もとの$appを実行しています。
リスト2 クライアントのIPを表示するPSGIアプリケーション
my $app = sub {
my $env = shift;
return [
200,
[ "Content-Type", "text/plain" ],
[ "Hello $env->{REMOTE_ADDR}" ],
];
};
リスト3 REMOTE_ADDRを上書きするミドルウェア
my $app = sub {
my $env = shift;
return [
200,
[ "Content-Type", "text/plain" ],
[ "Hello $env->{REMOTE_ADDR}" ],
];
};
my $mw = sub {
my $env = shift;
$env->{REMOTE_ADDR} = '1.2.3.4' if rand(1) > 0.5;
$app->($env);
};
ブラウザで開くと50%の確率で「Hello 1.2.3.4」と表示され、またその際のターミナルに表示されるアクセスログでも
1.2.3.4 - - [10/Jan/2010 21:05:17] "GET / HTTP/1.1" 200 13 ...
のように1.2.3.4と書き換わっているのがわかります。
実際にはあまり役に立たない例ですが、このように既存のアプリケーションに手を入れることなく、実行の前後にリクエストやレスポンスの中身をフックできるのがわかると思います。
また、この例ではラップされるPSGIアプリケーションをインライン($app)で記述していますが、こうしたミドルウェアはPSGIを実装したすべてのフレームワークで利用できます。Catalystなどのフレームワークでは多くの機能がプラグインやアクションといった拡張として用意されていますが、これらはCatalystでしか利用できませんでした。WebサーバのApacheに用意されている多くのmod_*モジュールも同様です。PSGIの仕様に沿ったミドルウェアでこうした拡張機能を記述すれば、PSGIを利用したサーバ、フレームワークで共有できます。
Plackには多くのミドルウェアが実装され、Plack::Middlewareという名前空間の下にバンドルされています。その使い方については後述します。
PSGI対応フレームワークでPlackで利用する
PSGIとPlackを発表して以来、多くのフレームワーク開発者がPSGI仕様へのフィードバックやPlackプロジェクトへの参加を行い、実際にフレームワークをPSGIに対応させる動きが進んでいます。
執筆時点(2010年1月)で、Perlのデファクトスタンダードと言えるCatalystをはじめ、古くから人気のあるCGI::ApplicationやMason、またJifty、Mojo、Squattingなどといったフレームワークが、PSGIにネイティブもしくは拡張プラグインを通して対応しています。HTTP::EngineについてもPSGIへのアダプタが用意されており、HTTP::Engineを利用しているアプリケーションやフレームワークは、コードを変更することなくPSGIへの移行パスが整っています。
Catalyst
Catalystは、CPANにリリースされているCatalyst::Engine::PSGI(筆者が開発、メンテナンスしています)を利用することでPSGIに対応することが可能です[1] 。
CPANから対応モジュールをインストールしたあと、次のようにプロジェクトのひな形を作成してください。
> catalyst.pl MyApp
以前なら、ここでCatalystに付属するスタンドアロンサーバを利用するために、
> ./script/myapp_server.pl
としていましたが、今回はまずPSGIアプリケーションの実行ファイルとなる.psgiのひな形を作成します。
> ./script/myapp_create.pl PSGI
created ".../MyApp/script/../script/myapp.psgi"
scriptディレクトリ以下にmyapp.psgiが作成されました。これをplackupツールで起動してみましょう。
> plackup script/myapp.psgi
...
[info] MyApp powered by Catalyst 5.80017
HTTP::Server::PSGI: Accepting connections at
http://0:5000/
このように、ポート5000でPlack付属のHTTP::Server::PSGIによってCatalystアプリケーションが起動しました。ブラウザでhttp://localhost:5000/
を開くと、図5 のように見慣れたCatalystのデフォルトページが表示されたはずです。
図5 Catalystをplackupで起動
ここではゼロからアプリケーションを作った場合を解説しましたが、既存のアプリケーションでもmyapp_create.pl PSGIを実行して.psgiファイルを作成すれば、同様にPSGI対応させることが可能です。
[1] 執筆時点で、CatalystコアチームによりCatalyst本体でPSGIに対応する作業が進んでおり、みなさんが読んでいるころにはすでに対応が終わっている可能性もありますが、基本的な利用のしかたは同じとなるはずです。詳細はオンラインドキュメントなどを参照してください。
そのほかのサーバで起動する
通常のPSGIアプリケーション同様、CatalystアプリケーションをPlackを利用してそのほかのWebサーバで起動することも可能です。
> plackup -s FCGI --listen /tmp/fcgi.sock script/ myapp.psgi
とすれば、UNIXソケットから読み込むFastCGIデーモンとしてlighttpdなどのWebサーバから呼び出すことができるでしょう。
Tatsumaki
PSGIにネイティブで対応しているフレームワークの例として、筆者が開発しているフレームワークTatsumaki を紹介します。
TatsumakiはPythonのTornado にインスパイアされて開発しているフレームワークで、PSGIのストリーミングや非同期I/O拡張に完全対応しており、epollやkqueueなどを利用したイベントループでサーバを起動し、非同期のアプリケーションを簡単に記述できます。
今回は紙幅の都合上、非同期機能を利用しない形で簡単なHello WorldをTatsumakiで記述してみます。最もシンプルなアプリケーションはリスト4 のようになります。Tatsumaki::Handlerを継承したハンドラクラスを定義し、Tatsumaki::ApplicationのnewでURLをマッピングします。このファイルをhello.psgiのような名前で保存し、
> plackup hello.psgi
とするとサーバが起動し、ブラウザでhttp://localhost:5000/hello
にアクセスすればいつものように「Hello World」が表示されるはずです。
リスト4 TatsumakiによるHello World
use strict;
package Hello;
use parent 'Tatsumaki::Handler';
sub get {
my $self = shift;
$self->write("Hello World");
}
use Tatsumaki::Application;
my $app = Tatsumaki::Application->new([
'/hello' => 'Hello',
]);
$app->psgi_app;
COLUMN 【PSGIとPlack】
PSGIはインタフェースの仕様で、Plackはそれを実装したソフトウェアの名前です。両者を別にするか同じにするかという議論はもちろんありました。
Pythonでは、WSGIという仕様があり、WSGIを実装したPaste、Pylons、WebObといったソフトウェアが別の名前であります。
それに対してRubyでは、インタフェース仕様もそれを実装したソフトウェアも同じ名前のRackです。さらに、ソフトウェアと仕様のバージョン番号が一致しているという構成になっています。これはRackが策定・実装された時期がWSGIよりもあとで、各種Rubyバージョンの差異などをあまり気にする必要がなかったことや、Railsが実質上のスタンダードになっていて、新しく作るRackが非Railsのスタンダードになっていくことが期待できた、というのが大きい気がしています。
PerlではPython同様、仕様とコードに別の名前を付けました。これにより、フレームワーク側で実装するものと、サーバ側で実装するものとの区別が明確にできるようになった、というメリットがあります。