最近のWindows IISでMojolicious::Liteを動かそうとすると、普通はCGIモードしか使えなくて全く実用的なパフォーマンスが確保できない。これは双方が互いを全くサポートしていないが故だが、これはそれをどうにかしてしまおうというメモ。
IISのCGIセットアップ
まず普通にIISでPerl CGIを動かすための手順。一応 Windows 10/Windows Server 2012 と ActivePerl 5.20(64bit) を前提にしているが、他の組み合わせでも大差はない。
コントロールパネル>プログラム>プログラムと機能>Windowsの機能の有効化または無効化と辿ってダイアログを開き、インターネットインフォメーションサービスにチェックを付ける。更にそのサブメニューを開いてWorld Wide Webサービス>アプリケーション開発機能>CGIにチェックを付ける。最低限これだけを有効化すれば要は足りるからOKを押して変更を確定する。初回導入時についてはサーバ再起動は必要ない。1
ActivePerl(64bit)が既にインストールされており、パスが通っているならコマンドプロンプトを管理者権限で開いて以下のコマンドをタイプする。
ap-iis-config add all --site 1 --cgi
するとIISマネージャーのサイト>Default Web Site>機能ビュー>ハンドラーマッピングに以下のスクリプトマップが登録されているはずだ。
復数のサイトが作られている場合、個別にこのスクリプトマップが必要になる。前述の--site 1の数字がサイトの登録番号を示しているので、これを変更しながら必要なだけ繰り返す。あるいはこのダイアログ画像を参考に手動でスクリプトマップを追加する。
なお32bit版のActivePerlでは、ISAPIを使用することもできる。Windows機能ダイアログでISAPIフィルターとISAPI拡張にもチェックをつけていれば、以下のコマンドでISAPIのハンドラーが作成できる。2
ap-iis-config add all --site 1 --cgi --isapi
以上で .pl ファイルはCGIとして認識されるようになる。.cgi ファイルを扱いたい場合は手動でスクリプトハンドラーを追加しよう。3
FastCGIを有効化する
FastCGIも同様にハンドラーマッピングを追加することで有効化できるが、ひとつのスクリプトに対してひとつのモジュールマップエントリが必要になる。例えば以下のスクリプトを登録してみよう。4
use strict;
use warnings;
use FCGI;
my $count = 0;
my $request = FCGI::Request();
while ( $request->Accept >= 0 ) {
print "Content-type: text/html\r\n\r\n", ++$count;
}
このファイルは通常のURIで辿れる場所に存在しなければならない。つまりC:¥inetpub¥wwwroot以下以外にあるのならば、仮想ディレクトリパスが通っている必要がある。そうした上で次のようにモジュールマップを登録する。
要求パス欄は実ファイル名と一致5させなければならない。モジュール欄はドロップダウンからFastCgiModuleを選択する。名前欄は同一サイト内で重複しなければ自由だ。そして実行可能ファイル欄にPerlの実行フルパス名とスクリプトのフルパス名"を半角パイプ記号で続けて記入する。なぜパイプで繋げるのかは解らないがこのように書かなければならない。これでOKを押すとハンドラを登録してよいか警告ダイアログが出るのではいで応答する。
正しくFastCGIハンドラが登録されていれば、このスクリプトをブラウザで開けば(例えば localhost/test/count.fcgi)カウンタが表示されるだろう。Ctrl+RやF5でリロードすればカウントアップが進むはずだ。
なおスクリプトを書き換えたあとは、実行中のFastCGIハンドラを再起動しなければならない。手っ取り早いのはタスクマネージャのプロセス一覧から、IIS Worker Process を探して タスクの終了 をさせてしまう方法だ。こうすればすぐに再度1からカウントをやり直す。
Mojolicious::LiteアプリをFastCGIで実行する
Mojoliciousは、その開発当初はともかく現在はIISをサポートしていない。だが前述のもっとも単純な FastCGIスクリプトを見ればわかるように、Requestループの中で普通にCGIスクリプトを利用すれば、そのままFastCGIプロセス (IIS Worker Process) として永続化される。かといってループ内で Mojolicious を呼び出してもそのままでは正常に動作しない。app->start が IIS/CGIであるかどうかを正しく認識できないためだ。そこで次のようなコードにする。6
use strict;
use warnings;
use Mojolicious::Lite;
plugin 'basic_auth';
my $count = 0;
get '/' => sub { $_[0]->render(text => 'Non restricted area' . ++$count); };
under sub {
my ($c) = shift;
return $c->basic_auth(
realm => sub {
if ( @_ && ( "@_" eq 'foo bar' ) ) {
$c->req->env->{REMOTE_USER} = $_[0];
return 1;
}
}
);
};
get '/rest' => sub {
$_[0]->render(text => 'Hello ' . $_[0]->req->env->{REMOTE_USER} . ++$count . join(",", @ARGV));
};
if ($ENV{APP_POOL_CONFIG}) {
use FCGI;
my $request = FCGI::Request();
while ( $request->Accept >= 0 ) {
app->start("cgi");
}
}
else {
app->start();
}
これはIIS固有の環境変数が存在するならば、FCGI::Requestループ実行に切り替えるようにしている。そうでなければ、つまりコマンドプロンプトで実行すれば、従来通りの挙動となる。こうしておいたうえで、FastCGIハンドラには次のように指定する。
C:¥Perl64¥bin¥perl.exe|C:¥test¥apps.fcgi cgi -m production
ここでCGI形式で実行することを強要し、かつproductionモードを強制する。このモード指定がないと developmentモードで実行されることになり、標準出力に非HTTPプロトコルな出力が混じってしまうので、結果IISはInternal Server Errorを返すことになってしまう。
apps.fcgiのある場所に logディレクトリを掘っておくとそこにログが吐き出されるので、モード指定がなくても一見問題が解決できるように見える。だが実運用段階ではこのログを定期的に削除する必要に迫られるので、ここではその方法は取らない。ログファイルは IIS Worker Process が握っているので、このプロセスを終了させないとログファイルを削除することができないのだ。他には IISの環境変数に MOJO_MODE を追加して回避する手段があるが、今回は設定が一箇所ですむ手法を取り上げた。7
機能を無効化した際はアンインストールのために再起動が必要になる。 ↩
64bit版ではISAPIはサポートされなくなった。もっともこれを使う機会じたいが滅多にないので、困る/困ったという話もおよそ聞いたことがない。 ↩
Strawberry Perlや Cygin Perl、その他CGIで使えるものはなんであれこのように手動で登録する。 ↩
mod_rewrite的な仮想パス機能ではないので、/などを指定することはできない。 ↩
FCGIモジュールは標準インストールされていないので事前に ppm install FCGI を実行しておく必要がある。 ↩
このサンプルはBASIC認証プラグインのテストも兼ねている。このプラグインは ppm install Mojolicious-Plugin-BasicAuth でインストールできる。ブラウザで apps.fcgi/rest を開くと認証ダイアログが出るのでユーザ名:foo パスワード:bar を入力してみて欲しい。 ↩
環境変数切り替えでは一見、復数のモードを切り替えて使い分けられるように見える。だがUnix環境と違ってIISマネージャをいちいち立ち上げないとその操作すら覚束ないのでメリットがない。結局必要なだけ個別のモジュールハンドラを登録してURIで使い分けたほうが簡単だったりする。 ↩