Perl Hackers Hub

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

前回の(1)こちらから。

PSGIサーバ

ここまで主にPSGIのアプリケーション側について見てきましたが、⁠2)ではアプリケーションを実行するためのPSGIサーバについて紹介します。

PSGIサーバの役割

PSGIサーバはPerlによるプログラムで、PSGIアプリケーションとアプリケーションが実行される環境を結び付ける役割をします。

図2がPSGIサーバの役割を図に表したものです。ApachenginxなどのWeb サーバからCGI、FastCGI、HTTPプロトコルを経由してクライアントからのリクエストがPSGIサーバに届きます。PSGIサーバはそれぞれの環境からPSGIのリクエスト情報を集めて$envを作り、PSGIアプリケーションに引き渡して実行します。実行後アプリケーションからレスポンスを受け取り、それぞれの環境に応じてレスポンスを整形して出力します。

図2 PSGIサーバの役割
図2 PSGIサーバの役割

PSGIサーバとPlackハンドラ

PSGIサーバはPlackハンドラと呼ばれることがありますが、両者は異なるものを指します。

Plackハンドラ[1]とは、PlackとPSGIサーバとを結び付けるアダプタのインタフェースであり、モジュールの名前空間ではPlack::Handlerを利用します[2]⁠。たとえば、PSGI 対応Web サーバのStarmanでは、Starman::ServerモジュールにPSGIサーバの実装があり、Plack とのアダプタインタフェースはPlack::Handler::Starmanに実装がされています。また、CGI、FastCGI、mod_perlなど既存のWebアプリケーション実行環境があり、独立したPSGIサーバの実装が必要ない場合、Plackハンドラに実行環境との橋渡しを実装してPSGIサーバを兼ねることがあります。

PSGIサーバの起動方法

Plackハンドラに対応したPSGIサーバはplackupコマンドを使うと簡単に起動できます。リスト2のようにアプリケーションをpsgiファイルに記述して、

$ plackup -a hello_world.psgi

として起動します。デフォルトではHTTP::Server::PSGIが使われ、TCPポート5000番をListenします。ブラウザで http://localhost:5000/にアクセスすると、⁠Hello WEB+DB PRESS」と表示されます。

リスト2 hello_world.psgi
#!/usr/bin/env perl
use strict;
use warnings;

my $app = sub {
  my $env = shift;
  # アプリケーションの処理
  return [
    200,
    ['Content-Type'=>'text/plain'],
    ["Hello WEB+DB PRESS\n"]
  ];
};

$app;

別のPSGIサーバを起動する場合は、plackup-sオプションを付けます。Starmanを使用する場合は次のようになります。

$ plack -s Starman --port 8080 -a hello_world.psgi

ここでは--portオプションも使い、ListenするTCPポートも変更しています。

よく利用されるPSGIサーバと、その選び方

PSGI/Plackの登場以降、さまざまなWebアプリケーションの実行環境がPSGIに対応したり、新しく作られました。2013年11月現在CPANには40個以上のPlack::Handlerが登録されています。その中から開発やプロダクション環境で利用されるPSGIサーバを紹介します。

HTTP::Server::PSGI

HTTP::Server::PSGIはplackupコマンドのデフォルトで使われるPSGI サーバです。Plack::Hander::StandaloneモジュールがPlack::Hander::HTTP::Server::PSGIのエイリアスとして実装されているので、PlackハンドラをStandaloneと指定しても動作します。

$ plackup -s Standalone -a helloworld.psgi

HTTP::Server::PSGIは単独でプロセスが1つしかないシングルプロセスのWebサーバとして起動し、使うリソースが少ないことから、アプリケーションの開発時に使われます。プロダクション環境での使用は、シングルプロセスのため同時に複数のリクエストを処理できず、性能も上がらないのでお勧めしません。

Starman─⁠─多機能PSGIサーバ

StarmanはNet::Serverモジュールをベースとした多機能なPrefork型のWebサーバです。作者はPSGI/Plackの開発や仕様策定を行っている宮川氏です。

Prefork型とは、図3のようにサーバ起動時にワーカと呼ばれる子プロセスを指定した数だけ起動fork(2)し、複数あるワーカでクライアントからのリクエストに応える方式です。最近では複数のCPUコアを搭載するサーバを利用することが多くなりますが、Prefork型は複数のコアを活かせる構造になっています。

図3 Prefork型サーバの動作
図3 Prefork型サーバの動作

Starman の特徴として、HTTP/1.1、SSLSecure Socket Layerへの対応、HTTPヘッダの処理をXS[3]で行うことでの高速動作、UNIXドメインソケットのサポート、Server::Starterを使った無停止デプロイなどが挙げられます。

Starmanはplackupでも起動できますが、付属のstarmanコマンドも使えます。

$ starman --workers 32 \
  --max-requests 500 \
  --preload-app \
  --disable-keepalive \
  -a helloworld.psgi

ここでは32個のワーカを起動し、それぞれ500リクエスト処理したらプロセスが終了して新しいプロセスに入れ替わるように--max-requestsを指定しています。アプリケーションにメモリリークがあっても一定の時間でプロセスを終了させることで、OSのメモリを食い潰すことを防ぎます。

--preload-appを指定すると、親プロセスでアプリケーションをメモリに読み込んでからワーカを起動します。親プロセスとワーカでメモリの一部が共有されるので使用メモリを減らせます。またアプリケーションで使用するモジュールのロードをワーカごとに行わずに済むので負荷の削減にもつながります。

Starmanはクライアントとサーバ間のTCP接続を使い回すHTTP KeepAliveがデフォルトで有効になりますが、Prefork型では接続の維持のためにワーカプロセスを占有するので、TCP接続が多くなると、すべてのワーカが接続の維持に使われ、新たなクライアントからのリクエストを受け付けることができなくなります。プロセスの枯渇を防ぐためPrefork型のサーバを使用する場合にはHTTP KeepAliveを無効にして運用するのが一般的です。Starmanでは--disable-keepaliveを指定します。

Starlet─⁠─シンプルで高速なPSGIサーバ

Starlet本連載第6回を執筆した奥一穂氏が開発しているPSGI対応のWebサーバです。Prefork型で、Linuxのネットワークプログラミングのテクニックが多数盛り込まれており、高速な動作を実現しています。HTTP::Parser::XSモジュールを導入するとXSでHTTPヘッダ処理が行われるので、特に理由のない限りインストールしてください。

StarletにはStarmanと異なり専用の起動コマンドが付属しないので、plackupでサーバを起動します。

$ plackup -s Starlet \
  --max-workers 30 \
  --max-reqs-per-child 1000 \
  --min-reqs-per-child 800 \
  --max-keepalive-reqs 1 \
  -a helloworld.psgi

Starletの起動オプションで特徴的なのが、--max-reqsper-child--min-reqs-per-childです。Starmanの--max-requestsに相当する機能ですが、ワーカプロセスの入れ替わるタイミングがmaxとminの間のランダムな値に設定されます。Prefork型のWebサーバでは、アクセスが多くなるにつれ、各ワーカが処理したリクエスト数がほぼ同数になり、プロセスの終了と新しいプロセスを起動が同じタイミングに集中します。アクセスの多いサイトではサーバの負荷が上がり、サービスに影響が出ることもあります。そこでStarletではワーカが入れ替わるタイミングにランダム性を持たせるオプションがサポートされています。

Twiggy─⁠─ AnyEventベースの非同期Webサーバ

Twiggyはイベント駆動ライブラリのAnyEventを利用したWebサーバです。StarmanなどのPrefork型のサーバとは異なり、1個のプロセスで数十~数百の接続を受けることができます。PSGIの遅延レスポンス、ストリーミングレスポンスをサポートしており、サーバプッシュ型アプリケーションに向いているPSGIサーバです。

リスト3はAnyEventを利用して、次のリクエストが来るまでレスポンスを待つPSGIアプリケーションです。

リスト3 twiggy_sample.psgi
use AnyEvent;

my $cv;
my $app = sub {
  my $env = shift;
  $cv->send if $cv;
  $cv = AE::cv;

  return sub {
    my $responder = shift;
    my $writer = $responder->([200, ['Content-Type' => 'text/plain']]);
    $writer->write("Wait for the next request..\n");
    $cv->cb(sub{
      $writer->write("request has come!\n");
      $writer->close;
    });
  };
};

リスト3は次のコマンドで起動します。

$ plackup -s Twiggy -a twiggy_sample.psgi

curlなどでアクセスすると、レスポンスが途中まで行われたのち次のリクエストを待つ状態に入ります。

$ curl http://localhost:5000/
Wait for the next request..

別のターミナルから同じcurlコマンドを実行すると「request has come!」と表示され、レスポンスが完了します。このようにTwiggyとPSGIを利用することで、簡単にイベント駆動のアプリケーションも書けます。

Twiggy::Prefork─⁠─ Prefork対応Twiggy

Twiggy::Preforkは拙作のWebサーバです。その名のとおりTwiggyにPreforkの機能を付加しています。Twiggyはすべての接続を1個のプロセス内で処理するので、クライアント同士の情報のやりとりが行いやすいという特徴があります。しかし、シングルプロセスではCPUコアを複数積んだサーバの処理能力を活かしきれません。

そこで開発をしたのがTwiggy::Preforkです。ワーカプロセスが複数になることで、クライアント同士の情報の交換は難しくなりますが、CPUのパワーを利用してさらに多くの接続を扱えます。

$ placket -s Twiggy::Prefork \
  --max-reqs-per-child 500 \
  --min-reqs-per-child 400 \
  -a delay_res.psgi

Twiggy::PreforkはStarletと同様に1プロセスが処理するリクエスト数の最大と最小を指定できます。

Feersum─⁠─高速非同期サーバ

FeersumはC言語のイベント駆動ライブラリであるlibevをベースに作られているPSGI対応のWebサーバです。C/XSで作られている部分も多く非常に高速に動作します。筆者は残念ながら使ったことはありませんが、WebアプリケーションのチューニングコンテストISUCON」において、このFeersumを使い上位にランキングしたチームがいます。

2013年11月現在、実験的な機能とされていますが、FeersumはPrefork型にも対応しています。

$ plackup -s Feersum \
  --pre-fork=4 -a delay_res.psgi

PSGIサーバの選び方

CPANには多くのPSGIサーバがありますが、ほかのミドルウェアに依存せず単独でWebサーバとして動作するPSGI サーバを使うのがお勧めです。HTTP::Server::PSGI、StarmanやStarletなど単独で動作するPSGIサーバは、perlのインタプリタをインストール・管理するソフトウェアであるplenv本連載Vol.75で紹介されたCartoncpanmなどを使い、簡単にセットアップできます。またWebサーバとして起動すればブラウザで直接アクセスできるので開発やデバッグも捗ります。

表2にPSGIサーバを選ぶ際の基準をまとめました。非同期処理を必要としないアプリケーションであれば1行目「通常のアプリケーション」を、非同期処理が必要なら2行目「非同期処理が必要なアプリケーション」となります。そして、シングルプロセス内でのデータの共有が必要なら1列目「シングルプロセス⁠⁠、そうでない場合は2列目「マルチプロセス」を選びます。多くの場合、マルチコアを持つサーバを使いますので、通常のアプリケーションの場合は自動的に右上のサーバになるでしょう。各カラムの中でどのサーバを選ぶべきかですが、実際のアプリケーションにおいてはどれも高速でそれほど差がありません。オプションの指定方法など開発者が慣れたサーバを選ぶのが良いでしょう。筆者の個人的なお勧めは、ホットデプロイ機能やプロセスごとに終了タイミングを変更できる機能を備えたStarletです。

表2 PSGIサーバの選び方
種類シングルプロセスマルチプロセス
通常のアプリケーションStarman
Starlet
非同期処理が必要なアプリケーションTwiggy
Feersum
Twiggy::Prefork
Feersum

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

おすすめ記事

記事・ニュース一覧