●apache に mod_perl を組み込む

●mod_perl とは?
Apache モジュール内にラップされた完全な Perl インタプリタ のことです。
通常 Perl で書かれたスクリプトを実行するには毎回 perl インタプリタを呼び出しコンパイル・実行されます。
ところが mod_perl の場合は一度呼び出された後はコンパイルされた状態のまま apache 内で次のリクエストを待ち続けます。
つまり perl スクリプトが apache の機能として実行されます。
一説には普通の perl スクリプトと比較すると200倍速いとされています。
他のメジャー言語である php などと比較しても速い実行結果があるようです。
巷では「perl は遅いから…」とよく言われてますが遅いという理由だけで C言語 や PHP などでコードを書く必要はありません。

apache + mod_perl これ最強です。


●インストール
既にApacheが DSO (Dynamic Shared Object)対応で構築済みの場合は、mod_perl モジュールのみ生成するだけです。
DSO 対応か調べるにはコンソールから
# httpd -l
Compiled-in modules:
http_core.c
mod_so.c
suexec: disabled; invalid wrapper /usr/sbin/suexec
上記のように mod_so.c が現れれば DSO に対応していることが分かり ます。

この様に DSO に対応していれば apache 本体を make する必要は ありません。ただモジュール形式ではなく apache 本体に組み込んでしまっ た方がレスポンスがいいと言う話も聞きますが、あえてそこまでする 必要もないので簡単なモジュール形式で組み込む事にします。
まず mod_perl 本体のダウンロードとコンパイルを行います。

ダウンロードはここから


$ tar xzvf mod_perl-x.xx.tar.gz
$ cd mod_perl-x.xx
$ perl Makefile.PL \
USE_APXS=1 \
WITH_APXS=/usr/sbin/apxs \
EVERYTHING=1
$ make
$ su
Password:
# make install

USE_APXS=1 は apxs (APache eXtenSion)を使って組み込む設定
WITH_APXS では apxs が存在するパスを指定します。
分からない場合はコンソールから
$ whereis apxs
apxs: /usr/sbin/apxs /usr/man/man8/apxs.8.gz
EVERYTHING=1 は全てのコンテンツハンドラを有効にします。

上記のインストールで libperl.so がライブラリにインストールされます。



●設定ファイルの編集
Apacheのコンフィギュレーションで、mod_perl 関連の設定を行う必要があります。
まずはモジュールのロードですが、DSO で入れた場合は以下のような設定になります。
LoadModule perl_module modules/libperl.so <--これは/usr/lib/apache/libperl.soと等価です
AddModule mod_perl.c

#mod_perlで実行するパスのエイリアスとそのロケーションセクションでPerlハンドラを設定します。

<IfModule mod_perl.c>

Alias /perl/ /home/httpd/perl/
Alias /cgi-perl/ /home/httpd/perl/

<Location /perl>
SetHandler perl-script
PerlHandler Apache::Registry
Options ExecCGI
Allow from all
PerlSendHeader On
</Location>

<Location /cgi-perl>
SetHandler perl-script
PerlHandler Apache::PerlRun
Options ExecCGI
Allow from all
PerlSendHeader On
</Location>

</IfModule>

上の設定では、'PerlSendHeader On' を指定しています。これは mod_perl のスクリプトでは標準では、HTTP の応答ヘッダを出力しないため、この指定でブラウザへの応答でヘッダ出力するように設定しています。ただし、nph(non-parsed-headers 非解析ヘッダ)スクリプトでは、スクリプトから応答を返す必要があるため、nph スクリプトを使用する場合には、Off にする必要があります。

実際には、スクリプト側で mod_perl 配下での実行有無を判定して、HTTP ヘッダを出力するようにしておいた方がよいかもしれません。そうすれば、nph スクリプトにも共通の処理で対応できます。(この例はポーティングの説明を参照)
なお、以下のようなファイルチェックでも nph スクリプトならヘッダ出力しないようにできます。

<Files */nph-*>
PerlSendHeader Off
</Files>

Apache::PerlRun ハンドラは、スクリプトおよびモジュールの Preload 機能を提供しますが、スクリプト自体はキャッシュされません。起動されるスクリプトは毎回コンパイルされて実行されます。実行後にはそのスクリプトの名前空間の変数やサブルーチンはフラッシュされます。(ただし、requireやuseされているモジュール内についてはフラッシュされません)したがって、通常のCGIスクリプト形式のままでの実行をエミュレートしてくれることになります。また、Perl インタプリタのロードと、標準モジュールロードのオーバヘッドがないという利点があります。

上の設定例では、Apache::Registry と Apache::PerlRun ともに同じディレクトリを指しています。これは要求URLの(エイリアス)記述の違いだけで、いずれかのPerlハンドラを選択可能にするテクニックです。同様に cgi-bin も同じディレクトリを指すようにエイリアス指定すれば、cgi-bin の場合は全く通常のCGIとしてスクリプトが実行されることになります。

mod_perlでは事前にモジュールをロードしておくことが可能です。

PerlModule Apache::DBI CGI DBD::Pg
または、
PerlRequire /etc/httpd/conf/startup.pl

mod_perl にはこれら以外にも多くの機能があります。
ここで全ては説明できませんが、 mod_perl関連の設定が多い場合には、httpd.conf 内に直に記述せずに別ファイルとして、 httpd.conf に Include 文でマージする方法の方が管理しやすいでしょう。

※詳しくはO'Reilly ジャパン発行の「Apache拡張ガイド」をご覧下さい。


●スクリプトの実行
ロケーションセクションで指定したディレクトリにスクリプトを設置し、エイリアスを指定してスクリプトを起動することで、 mod_perlでの実行が可能です。

/home/httpd/perl ディレクトリにある、test.pl を Apache::Registry 配下で実行するには、以下のようなURL指定になります。

http://domain_name/perl/test.pl
同様に以下のような指定で、Apache::PerlRun での実行を指定できます。

http://domain_name/cgi-perl/test.pl
また、通常の CGI は mod_perl 用エイリアス以外を指定することで実行可能です。
http://domain_name/cgi-bin/test.pl
http://domain_name/~xxxxxx/xxxxx/hoge.pl

※cgiファイル互換で動かす場合は以下のように
<Files *.cgi>
SetHandler perl-script
PerlHandler Apache::Registry
PerlSendHeader On
</Files>


●CGI版と mod_perl版 の実行動作の違いについて
前にも言いましたが mod_perl に於いてはスクリプトを実行可能状態にコンパイルしてメモリに保持 し続けるがまたそれと同時にスクリプト内で使われる変数に於いても同様の事が起こる。 これは一見なんの問題も無いように思えるがスクリプトの作り方次第では問題が起こることが多い。
実際にサンプルを使って検証してみる。
count.pl:

#!/usr/bin/perl -w
use strict;

print "Content-type: text/plain\n\n";

my $count = 0;

for (1..5) {
increment();
}

sub increment{
$count++;
print "Count is $count \n";
}
上のスクリプトは単にカウンタをインクリメントして表示するプログラムである。
これをCGIとして実行すると次の様な結果になる。
※何度実行しても同じ答えが出るはず。
Count is 1
Count is 2
Count is 3
Count is 4
Count is 5
次に mod_perl 環境で実行するとどうなるだろうか、1度目は上と同じだが2度3度と繰り返すと 値がどんどん増えていってしまう。これでは明らかに目的とは違うスクリプトになってしまう。
Count is 6
Count is 7
Count is 8
Count is 9
Count is 10
これは CGI ではプログラムの終了をもって全てメモリがクリアされるのに対し、 mod_perl 環境に於いてはスクリプトと取得したメモリを保持し続ける事が原因である。
じゃ、どうすればいいんだい?と言われるがスクリプト内では変数を 必ず初期化して尚かつ変数のスコープを限定して利用すれば良いはずである。

さらに言うと use strict; を使い -w オプションを付けて実行しerrorログに何も出てこなければOKなはずだ。
因みに上記のスクリプトを実行すると error ログには次の様に記録される。
Variable "$count" will not stay shared at /home/ishot/public_html/count.pl line 13.