apache に mod_perl を組み込む

mod_perl とは?

Apache モジュール内にラップされた完全な Perl インタプリタ のことです。通常 Perl で書かれたスクリプトを実行するには毎回 perl インタプリタを呼び出しコンパイル・実行されます。ところが mod_perl の場合は一度呼び出された後はコンパイルされた状態のまま apache 内で次のリクエストを待ち続けます。つまり perl スクリプトが apache の機能として実行されます。

一説には普通の perl スクリプト(cgi)と比較すると200倍速いとされています。他のメジャー言語である php などと比較しても速い実行結果があるようです。実際にphpやjavaとでベンチマークをとりテストした方もいるようです。

JavaはPerlよりも比較にならないほど速い?

巷では「perl は遅いから...」とよく言われてますが遅いという理由だけで C言語 や PHP などでコードを書く必要はありません。mod_perl にはスッゴイ機能があるんです。

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 が存在するパスを指定します。分からない場合はコンソールから

$ which apxs
/usr/local/sbin/apxs

EVERYTHING=1 は全てのコンテンツハンドラを有効にします。

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

設定ファイルの編集

Apacheのコンフィギュレーションで、mod_perl 関連の設定を行う必要があります。まずはモジュールのロードですが、DSO で入れた場合は以下のような設定になります。

LoadModule perl_module modules/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 スクリプトならヘッダ出力しないようにできます。

PerlSendHeader Off

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

上記は

use Apache::DBI;
use CGI;
use DBD::Pg;

した場合と同じ効果が得られます。

apache を起動と同時にperl script を起動させたい場合は以下のsyntax でperl script を実行させる事が可能です。 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指定になります。

location /perl ではブラウザからのアクセスは

http://host_address/perl/test.pl

で Apache::Registry モードで動作します。

CGI 互換モードであるApache::PerlRun は以下のアドレスでアクセスします。

http://host_address//cgi-perl/test.pl

また、通常の CGI は mod_perl 用エイリアス以外を指定することで実行可能です。

http://domain_name/cgi-bin/test.pl
http://domain_name/~xxxxxx/xxxxx/hoge.pl

※cgi拡張子でモードを指定してmod_perlで動かす場合は以下のようになります。

<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.

余談になりますが上記説明はmod_perlの2つのモード Registry と PerlRun の説明ですが実際にはもう一つのモードがあります。

Perl Handler と呼ばれる機能が3つめの機能ですがこちらはmod_perl の本質ではないかと自分的には思います。

上での機能説明ではあくまで既存のscriptの実行を速くする事に終始していますがこの perl handler ではapacheのリクエストからはじまり コンテンツ の response の間にある任意のフェーズにperl script で介入する事が可能になる訳です。

ここで説明しきれない程の機能がperl handler にはあります。詳しくはApache拡張ガイド上下に詳しく載っているので読んでみて下さい。

created:

Back to top