Perl Hackers Hub

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

本連載では、第一線のPerlハッカーが回替わりで執筆していきます。記念すべき第1回は、WEB+DB PRESS本誌ではVol.2から執筆しており、長らく連載も担当していた宮川達彦さんです。

はじめに

PerlでWeb開発をするためのフレームワークは百花繚乱、人気を集めています。本稿では、これらのフレームワークが共通して利用するためのインタフェース仕様であるPSGIと、そのエンジンとしての実装であるPlackを紹介します。

PSGIに至る道

PerlとWebアプリケーション開発の親和性

Perlは「インターネットのグルー(糊:のり)言語」とも言われ、CGIによる開発がメインだった1990年代から、Webアプリケーション開発に最も関わりのあるプログラミング言語の一つと言ってよいでしょう。2000年代に入っても、Ruby on RailsやPHPなどの他言語からの影響も取り入れながら、Web開発との親和性は依然として高いままです。YAPC::Asiaといったカンファレンスなどでも、Webに関するトラックが数多く見受けられます。

WebフレームワークとWebサーバ環境の多様化

10数行の書き捨てCGIスクリプトならともかく、ある程度の規模でWebアプリケーションを開発するなら、Webフレームワークを使うことはもはや常識でしょう。

人気のあるPerl Webアプリケーションフレームワークとしては、古くはCGI::Applicationや筆者も開発に関わったSledgeから、Railsブームなどの影響を受けた大規模フレームワークとしてCatalystJiftyなどが挙げられます。どのフレームワークにもそれぞれ特長やポリシーがあり、どれか1つだけが進化してほかのフレームワークは淘汰されるべき、とは筆者は思いません。Perlのモットーでもある「やり方は一つでない」There's More Than One Way To Do Itが実現され、いくつも選択肢がある状況が望ましいでしょう。

ただ、フレームワークが乱立すると困ることがあります。それは、Webアプリケーションをデプロイして実行するWebサーバも1つではないことです。たとえばCatalystで書いたWebアプリケーションを実行する環境としては、付属のスタンドアロンサーバをはじめ、Apache(CGI、mod_perl⁠⁠、lighttpd(FastCGI⁠⁠、nginxなど、さまざまなWebサーバがサポートされています。これは、そうした多様なWebサーバ環境ごとの差異を吸収するためのコードをCatalystがすべて自前で書いている(Catalyst::Engineとそのサブクラスがそれに相当)ということです。こうしたWebサーバ固有の処理を一つ一つフレームワーク側で書かなければいけない状況は、けっして理想的ではありません図1⁠。

図1 フレームワークとWebサーバ
図1 フレームワークとWebサーバ

これを解決するために、Catalyst::Engineをほかのフレームワークから利用可能にするHTTP::Engineというプロジェクトがあり、筆者も参加していましたが、モジュールとしてのコードの実装とインタフェースが切り離されていないため、なかなか既存の(Catalyst以外の)フレームワークが移行するのは難しい状況でした。

WSGIとRackによる解決

筆者がPerlプログラミングやWebアプリケーション全般のデザインで悩んだとき、⁠PythonやRubyではどうしているだろう?」と考え、実装や仕様を見てみることにしています。こうしたWebフレームワークとサーバの分離については、PythonやRubyもフレームワークが乱立して同様の問題を抱えていましたが、PythonではWSGIWeb Server Gateway InterfaceRubyではそれに影響されたRackというWebフレームワークとWebサーバをつなぐ共通インタフェースを定義することで、多くのフレームワークがコードを重複させることなく、Webサーバ環境に対応することができています。実際に、PythonのDjango、web.py、CherryPy、RubyのRails、Merb、SinatraなどのフレームワークがWSGIやRackに対応しています。

PSGI─⁠─Perl版共通インタフェース仕様

これをそのままPerlに持ってこよう、と筆者が始めたのがPSGIです。HTTP::EngineやCatlaystの開発者たちとIRCInternet Relay Chat上でブレインストーミングを重ね、2009年10月に1.0の仕様をリリースしました。PSGIはPerl Web Server Gateway Interfaceの略で、もちろんWSGIやRackからインスパイアされたインタフェースとなっています。

このインタフェース仕様をアプリケーション側で実装し、そして各種Webサーバとつなぐためのアダプタを書くことで(あるいはPSGIを直接実行するWebサーバをPerlで実装することで⁠⁠、図1のような混沌とした状態は整理され、図2のように共通化できるようになります。新しくWebアプリケーションフレームワークを書く際に、既存の各種Webサーバに対応するコードを書く必要はありませんし、新しくWebサーバのサポートを追加する際にも、PSGIへのアダプタだけ書けばどのフレームワークも実行できるようになります。

図2 PSGIによるインタフェース共通化
図2 PSGIによるインタフェース共通化

PSGIアプリケーション

PSGIのアプリケーションはリスト1のようになります。アプリケーションはコードリファレンス(サブルーチンリファレンスとも呼ばれます)として表され、引数としてアプリケーションの実行環境を表すハッシュリファレンスの$envが渡されます。これはCGIやFastCGI実行時に渡される環境変数に非常に似ていますが、POST/PUTリクエスト時にボディを読み込むためのハンドルやエラー出力のためのI/Oなどが追加されています。アプリケーションの処理が終了したら、ステータスコード、ヘッダを格納する配列リファレンス、ボディを格納する配列リファレンスまたはI/Oオブジェクトを3要素の配列リファレンスとして返します。

リスト1 PSGIによるHello World
my $app = sub {
    my $env = shift;
    return [
        200,
        [ 'Content-Type' => 'text/plain' ],
        [ "Hello World" ],
    ];
};

HTTP::Engineはこの$envやレスポンス配列の代わりにリクエスト/レスポンスをAPIで定義しています。これはたしかにエンドユーザには使いやすいのですが、既存フレームワーク側で実装しなければいけないことが多く、またHTTP::Engineやそれが依存しているライブラリ(Any::Moose、HTTP::Bodyなど)に間接的に依存してしまう、という問題がありました。

PSGIで重要なのは、このコードリファレンスはシンプルなPerlコードであり、PSGIを実装するために、アプリケーション側もサーバ側も、どのようなモジュールにも依存する必要がないということです。また、既存のフレームワークのほとんどはすでにCGIに対応している場合がほとんどのため、$envがCGIの環境変数に似ているPSGIを追加でサポートするのが容易になるというメリットもあります。

おすすめ記事

記事・ニュース一覧