Perl Hackers Hub

第24回PSGI/Plack実践入門―Starman、Starlet、Twiggy、Plack::Middleware、Server::Starter(1)

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはWEB+DB PRESS Vol.50~55まで「大規模Webサービスの裏側」を連載し、本連載でも第11回以来の2度目の登場となるkazeburoさんこと長野雅広さんで、テーマはPSGI/Plackです。

なお本稿のサンプルコードは、本誌サポートサイトから入手できます。

幅広く使われるようになったPSGI/Plack

本連載でPSGIPerl 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のWSGIWeb Server Gateway InterfaceRubyの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_PROTOCOLHTTP/1.1などのプロトコル
HTTP_*リクエストヘッダ
psgi.inputPOSTデータを読み込むためのファイルハンドルオブジェクト
psgi.errorsエラーに使うファイルハンドルオブジェクト
psgi.url_schemeリクエストのURIスキーマ(httpやhttpsなど)
psgi.versionPSGI仕様のバージョン
psgi.multiprocessPSGIサーバがサポートしている機能についてのフラグ
psgi.streaming
psgi.nonblocking

レスポンス

現在のPSGIの仕様では2種類のレスポンスが規定されています。一つは標準的な配列のリファレンス、もう一つが遅延レスポンスやストリーミングに使用されるコードリファレンスによるレスポンスです。

標準レスポンス

PSGIの標準的なレスポンスは配列のリファレンスで表現します。配列は3つの要素から構成されます。

my $res = [
  200, ――(1)
  [                                
    "Content-Type"=>"text/plain",  
    "Content-Length"=> 13,         |―(2)
  ],                               
  [                            
    "Hello World\n",           
    "WEB+DB PRESS\n"           |―(3)
  ]                            
];

(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; ――(1)

    http_get('http://gihyo.jp/',sub {
      my ($body, $header) = @_;
      $responder->([ 200, [], [ $body ] ]); ――(2)
    });
  };
};

PlackとPSGI

本節の最後としてPlackとPSGIの関係を整理します。PSGIはここまで書いてきたとおり、PerlのアプリケーションとWebサーバとをつなぐ仕様です。対してPlackは、PSGI仕様のリファレンス実装となっています。Plackのディストリビューションには、PSGIリクエスト/レスポンスを扱うためのモジュール、PSGI対応サーバ、テストスイートが含まれています図1⁠。

PlackはPSGIの1つの実装ですので、PSGI 対応のWebアプリケーションフレームワーク、サーバを書くにあたって、必ずしもPlackを使う必要はありませんが、多くのモジュールがPlackの提供する豊富なユーティリティ群を利用して実装されています。

図1 Plackが提供するモジュール一覧(一部)
図1 Plackが提供するモジュール一覧(一部)

<続きの(2)こちら。>

おすすめ記事

記事・ニュース一覧