本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはWEB+DB PRESS Vol.50~55まで「大規模Webサービスの裏側」を連載し、本連載でも第11回 以来の2度目の登場となるkazeburoさんこと長野雅広さんで、テーマはPSGI/Plackです。
なお本稿のサンプルコードは、本誌サポートサイト から入手できます。
幅広く使われるようになったPSGI/Plack
本連載でPSGI (Perl Web Server Gatewary Interface )/Plackについて取り上げるのは2回目です。1回目は2010年2月に発売されたWEB+DB PRESS Vol.55 に掲載されたもので、本連載の第1回 でもありました。PSGIの仕様について議論が始まったのが2009年秋ですので、仕様が決まりPlackの実装が出て間もないころの記事でした。内容はPSGIの仕様策定を主導した宮川達彦氏によるPSGI仕様や仕様策定に至った経緯、リファレンス実装やユーティリティモジュールを備えるPlack、ミドルウェアの紹介でした。
それから4年経ち、今では多くのWebアプリケーションフレームワークがPSGIに対応し、Plackもブログサービス、ソーシャルゲーム、広告システムなど規模にかかわらずさまざまなWebサービスで使われており、PSGI/Plackは当たり前の存在となりました。本稿では、幅広く利用されるようになったPSGI/Plackの実践入門として、Webアプリケーションの構築・運用の現場で使われるノウハウを紹介します。
PSGIの仕様の復習
PSGIはWebアプリケーションと実行環境のWebサーバとをつなぐための仕様です。PythonのWSGI(Web Server Gateway Interface ) やRubyのRack からインスパイアされたインタフェースとなっています。インタフェースが標準化されたことで、利用しているWebアプリケーションフレームワークやWebアプリケーションがPSGIに対応していれば、開発者が特に意識しなくてもPSGIに対応しているさまざまなWebサーバ、実行環境をそのまま利用できます。
ここでは2013年11月時点の最新であるバージョン1.1に基づいて、PSGIの仕様を簡単に振り返ります。
PSGIアプリケーション
以下が最もシンプルなPSGIに対応したアプリケーションです。
my $app = sub {
my $env = shift;
# アプリケーションの処理
return [200,
['Content-Type'=>'text/plain'],
["Hello World\n"]
];
};
PSGIアプリケーションはPerlのコードリファレンスとして実装します。実行時に引数として$env
と呼ばれるリクエストの情報が入ったハッシュリファレンスが渡されます。レスポンスは配列のリファレンスを返しています。
リクエスト
リクエスト情報が入るハッシュリファレンスには表1 のようなデータが含まれています。大きく分けて、
CGI仕様(RFC 3875 )で規定されるリクエスト情報
POSTリクエストのデータ読み込みや、ファイルハンドルやエラー出力のためのファイルハンドル
PSGIのオプション
の3つになります。通常のハッシュリファレンスですので、
my $ua = $env->{USER_AGENT};
とすることでデータにアクセスできます。
表1 主なリクエスト情報
キー 内容
REQUEST_METHOD リクエストメソッド
REQUEST_URI リクエストがあったURI
QUERY_STRING リクエストのクエリパラメータ
SERVER_PROTOCOL HTTP/1.1などのプロトコル
HTTP_* リクエストヘッダ
psgi.input POSTデータを読み込むためのファイルハンドルオブジェクト
psgi.errors エラーに使うファイルハンドルオブジェクト
psgi.url_scheme リクエストのURIスキーマ(httpやhttpsなど)
psgi.version PSGI仕様のバージョン
psgi.multiprocess PSGIサーバがサポートしている機能についてのフラグ
psgi.streaming
psgi.nonblocking
レスポンス
現在のPSGIの仕様では2種類のレスポンスが規定されています。一つは標準的な配列のリファレンス、もう一つが遅延レスポンスやストリーミングに使用されるコードリファレンスによるレスポンスです。
標準レスポンス
PSGIの標準的なレスポンスは配列のリファレンスで表現します。配列は3つの要素から構成されます。
my $res = [
200,
[
"Content-Type"=>"text/plain",
"Content-Length"=> 13,
],
[
"Hello World\n",
"WEB+DB PRESS\n"
]
];
(1) の要素が3桁の数字のHTTPのステータスコードです。(2) の要素がレスポンスのHTTPヘッダで、ヘッダ名、内容の順に配列のリファレンスに格納します。(3) の要素がレスポンスのボディです。ボディは配列のリファレンスもしくはファイルハンドルで指定します。
open my $fh, "</path/to/file";
my $res = [ 200,
["Content-Type"=>"image/jpg,],
$fh ];
容量の大きなコンテンツを返す際に、ファイルの内容を読み込まずにファイルハンドルを利用することで、メモリの節約ができます。
遅延レスポンス
PSGIでサポートされるもう一つのレスポンスの形式が、コードリファレンスによる遅延レスポンス、またはストリーミングレスポンスです。遅延レスポンスを利用すると、別の処理の完了を待ってからレスポンスを返すことができます。ストリーミングレスポンスを利用すると、イベントが発生するたびにコンテンツを返すサーバプッシュ型のアプリケーションを作成できます。
リスト1 は、非同期でHTTP通信を行うAnyEvent::HTTP モジュールを使い、Webサイトからコンテンツが返ってくるまでレスポンスを遅延するアプリケーションです。レスポンスにコードリファレンスを指定した場合、そのコードリファレンスはresponderと呼ばれる引数(コードリファレンス)が渡されて実行されます(リスト1(1) ) 。そしてWebサイトからコンテンツが得られたあと、( リスト1(2) )にて通常の配列のリファレンス形式のレスポンスを作成し、responder
に渡して実行します。ここで初めてクライアントにレスポンスが返ります。
リスト1 遅延レスポンスの例(delay_res.psgi)
use AnyEvent::HTTP;
my $app = sub {
my $env = shift;
return sub {
my $responder = shift;
http_get('http://gihyo.jp/',sub {
my ($body, $header) = @_;
$responder->([ 200, [], [ $body ] ]);
});
};
};
PlackとPSGI
本節の最後としてPlackとPSGIの関係を整理します。PSGIはここまで書いてきたとおり、PerlのアプリケーションとWebサーバとをつなぐ仕様です。対してPlackは、PSGI仕様のリファレンス実装となっています。Plackのディストリビューションには、PSGIリクエスト/レスポンスを扱うためのモジュール、PSGI対応サーバ、テストスイートが含まれています(図1 ) 。
PlackはPSGIの1つの実装ですので、PSGI 対応のWebアプリケーションフレームワーク、サーバを書くにあたって、必ずしもPlackを使う必要はありませんが、多くのモジュールがPlackの提供する豊富なユーティリティ群を利用して実装されています。
図1 Plackが提供するモジュール一覧(一部)
<続きの(2)はこちら 。>