Perl Hackers Hub

第1回PSGI/Plack―フレームワークとサーバをつなぐエンジン (2)

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 スタックトレースの表示
図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ミドルウェア
図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で起動
図5 Catalystをplackupで起動

ここではゼロからアプリケーションを作った場合を解説しましたが、既存のアプリケーションでもmyapp_create.pl PSGIを実行して.psgiファイルを作成すれば、同様に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;

おすすめ記事

記事・ニュース一覧