2005-05-28

mod_perl

mod_perlはPerlの機能を最大限利用するためのApacheの拡張モジュールです。mod_perlは、

http://perl.apache.org/ The Apache/Perl Integration Project

で開発されています。mod_perlは、Perlコードをロード時に1度だけコンパイルし、その後もメモリに常駐させます。従って、非常に速い動作が可能になります。mod_perlの利用はPerl/CGIの使用者から見れば魅力的です。しかし、Perlスクリプトが常駐するメモリ消費量の問題や、Perl/CGIとの互換性の問題もあり、mod_perlを利用できるプロバイダは現在のところ大変少ないのが現状です。

本編ではPerl/CGIのテスト環境の構築を主な目的としていますが、mod_perlについても実験的にテスト環境を構築します。使用するmod_perlのバージョンは、

mod_perl 2.0

です。このバージョンに対応しているApacheとActivePerlは

Apache 2.0
ActivePerl 5.8.x (ActivePerl build 8xx)

です。mod_perl Windows版に関する情報はperl.apache.org 内の以下のURLから入手できます。

http://perl.apache.org/docs/2.0/os/win32/install.html

mod_perlのインストール

Apache Windows版にmod_perlをインストールするにはPPMを使用します。mod_perl 2.0を公開しているPPMリポジトリは、perl.apache.orgでも紹介されている

http://theoryx5.uwinnipeg.ca/ppms/ ウィニペグ大学(カナダ)

です。このリポジトリはmod_perlの他にもGD(Perlでグラフを作成するモジュール)などのPPMパッケージを公開しています。

PPMを使えばmod_perlのインストールは大変簡単です。コマンドプロンプトからppmコマンドを実行するだけですが、その前に1つだけ確認しておく事があります。それは mod_perl.so(Apacheの拡張モジュール)のインストール先です。本編ではApache Windosw版のインストールフォルダに合わせて以下のフォルダにmod_perl.soをインストールします。

C:\usr\Apache2\modules

mod_perlのインストールはppmを使用して次のように行います。

C:\usr>ppm install http://theoryx5.uwinnipeg.ca/ppms/mod_perl.ppd
  ・
  ・
  ・
The Apache2 module mod_perl.so is needed to complete the installation,
and should be placed in your Apache2 modules directory. I will
now fetch and install this for you.

Fetching http://theoryx5.uwinnipeg.ca/ppms/x86/mod_perl.so ...  done!
Where should mod_perl.so be placed? [D:/Apache2/modules] C:\usr\Apache2\modules
mod_perl.so has been successfully installed to C:/usr/Apache2/modules.
To enable mod_perl, put in the directives
   LoadFile "C:/Path/to/Perl/bin/perl58.dll"
   LoadModule perl_module modules/mod_perl.so
in httpd.conf, and also either use a directive
'PerlModule Apache2' or add 'use Apache2 ();' in
a startup script. For more information, visit
   http://perl.apache.org/

Successfully installed mod_perl version 2.0.0-RC4 in ActivePerl 5.8.6.811.

コマンド(ppm install)を実行して、しばらくすると、mod_perl.soのインストール先を聞いてきます。

Where should mod_perl.so be placed?

ここでは C:\usr\Apache2\modules を指定していますが、Apacheのインストール環境に合わせて入力して下さい。

mod_perlのためのApacheの設定

mod_perlを使用するためにはApacheの設定ファイル(httpd.conf)を編集します。本編では特定のURLパス(http://localhost/mod/)への要求に対して、mod_perlが使かえるようにします。尚、mod_perl のApache設定に関する情報はperl.apache.org 内の以下のURLから入手できます。

http://perl.apache.org/docs/2.0/user/config/config.html mod_perl 2.0 サーバ設定
http://perl.apache.org/docs/2.0/os/win32/config.html mod_perl 2.0 Windows版用

Apacheモジュール(mod_perl)の追加

### Section 1: Global Environment
  ・
  ・
LoadFile "C:/usr/bin/perl58.dll"
LoadModule perl_module modules/mod_perl.so

httpd.conf「Section 1: Global Environment」内の「Dynamic Shared Object (DSO) Support」の最後に以下の行を追加します。

LoadFile "C:/usr/bin/perl58.dll"      perl58.dllをサーバ起動時にロードします
LoadModule perl_module modules/mod_perl.so mod_perl.soを有効なモジュールリストに追加します

ドキュメントルートのアクセス制御

<Directory "D:/WWWRoot">
    Options All
    AllowOverride All
    Order Allow,Deny
    Allow from localhost
    Allow from 192.168.11.0/24
</Directory>

Options Allディレクティブを使って、インデックス作成、シンボリックリンク、CGI/SSIの実行など全てのオプションを許可します。本編でのアクセス制御についての考え方は「ActivePerl Windows版の使い方;Perl/CGIのためのApacheの設定」を参照して下さい。

ここまでの設定で、mod_perl設定のための準備は終わりました。いよいよ本題に入ります。

mod_perlの設定

まず、mod_perlの設定の為のhttpd.confの内容を以下に示します。

#
# Configuring mod_perl 2.0
#
<IfModule mod_perl.c>
PerlRequire "C:/usr/Apache2/conf/startup.pl"
<Directory "D:/WWWRoot/mod">
  <Files ~ "\.(cgi|pl)$"> 
    SetHandler perl-script
    PerlInitHandler Apache::Reload
#   PerlSetVar ReloadAll Off

    PerlResponseHandler ModPerl::PerlRun
#   PerlResponseHandler ModPerl::Registry

    PerlOptions +ParseHeaders
  </Files>
</Directory>
</IfModule>

特定のURLパス(http://localhost/mod/)内のPerlスクリプトファイル(拡張子が.cgiと.pl)への要求に対して、CGIでなくmod_perlが使つかえるように設定しています。

<IfModule>,<Directory>,<Files>の順にブロックを定義します。<IfModule mod_perl.c>は

LoadModule perl_module modules/mod_perl.so

と関連付けられています。mod_perl.soが有効な場合に<IfModule>ブロック内が評価されます。尚、<IfModule>内のモジュール名はmod_perl.soでない点に注意して下さい。これはコンパイルをした時のファイル名(mod_perl.c)になります。

スタートアップファイルの指定

PerlRequire "C:/usr/Apache2/conf/startup.pl" 

PerlRequireディレクティブはPerlのrequre関数と同じ働きをします。ここでは、

C:/usr/Apache2/conf/startup.pl

を指定してPerlを起動しています。mod_perlのスタートアップについては以下のURLを参照して下さい。

http://perl.apache.org/docs/2.0/user/handlers/server.html#mod_perl_Startup

スタートアップファイル(ここではstartup.pl)にはサーバ起動時に実行するPerlコードを記述します。スタートアップファイルには、

  • Perlモジュール(.pmや.pl)の検索パス(@INC)の追加
  • 共通に使用するモジュールのプリロード
  • 共通に使用する定数の定義

などを記述します。スタートアップファイルを使用せすにPerlを起動する方法の1つは、PerlRequireの代わりに、

PerlModule Apache2

と書く事です。これはPerlを起動する必要最低限の方法です。このようにPerlのスタートアップに関する記述を、ディレクティブを使ってhttpd.confの中でも書けますが、スタートアップファイルで記述しておいた方が便利です。

本編で実験用に使用するスタートアップファイルを以下に挙げます。

startup.pl

# startup.pl
use Apache2 ();

use ModPerl::Util ();
use Apache::RequestRec ();
use Apache::RequestIO ();
use Apache::RequestUtil ();
use Apache::ServerRec ();
use Apache::ServerUtil ();
use Apache::Connection ();
use Apache::Log ();
use Apache::Const -compile => ':common';
use APR::Const -compile => ':common';
use APR::Table ();
#use Apache::compat ();
use ModPerl::Registry ();
use ModPerl::PerlRun ();
use CGI ();

use lib "D:/WWWRoot/mod";
use lib "D:/WWWRoot/mod/mod_test";
1;

最初にApache2.pmをロードします。Apache2モジュールは必ず最初にロードしなけらばならないモジュールです。Apache2は以降にロードするモジュールの検索パスを@INCに追加します。Apache2以外のモジュールをここで必ずロードする必要はありません。あくまでも起動時のプリロードです。Apache::compatモジュールはmod_perl 1.0との互換性を提供します。

次に、自前の環境を記述します。ここでは実験に使用するフォルダにモジュールの検索パスを通しています。以下のような観点からスタートアップファイルにモジュールの検索パスを追加する事を勧めます。

スタートアップファイルにuse libを書く理由

mod_perlでは基本的に、サーバ起動の後は@INCは凍結されて変更できません。但し、クライアントのリクエストによりスクリプトまたはモジュールがロードされコンパイルされる時は、一時的に@INCを(use libプラグマを使用して)変更できます。しかし、メモリ内にキャッシュされているコードを実行する時には、@INCはオリジナルの値にリセットされています。例えば、次のようなコードを考えて下さい。

use lib "D:/WWWRoot/mod/mod_test";
if ($ENV{QUERY_STRING}){
  require MyModule;
}

1回目のリクエストにクエリ文字列が無い場合MyModuleはロードできていません。引き続いて、キャッシュされているコードを実行する時には、既に@INCにはリセットされているので、クエリ文字列が指定されていてもMyModuleをロードする事ができません。この事は、ModPerl::Registryを介して 1度しかコンパイルされないスクリプトを実行する場合に特に問題となります。これに対してModPerl::PerlRunの場合は毎回スクリプトがコンパイルされるので問題になる場面は少ないと思われますが、ModPerl::PerlRunの場合でも、スクリプトから呼び出される(package宣言の有る)モジュールは1度しかコンパイルされないので注意が必要です。

モジュールの検索パスをスタートアップファイル追加する理由は他にもあります。通常、@INCの最後には. (カレントディレクトリ)があります。mod_perlの場合も同じですが、実行時のカレントディレクトリはApacheのインストールフォルダになっています。従って、スクリプトファイルが存在する場所にモジュールの検索パスが通っていないのです。この問題は、Cwd::chdir() を使用してカレントディレクトリを変更すれば一時的には解決できます。しかし、このカレントディレクトリはmod_perl環境で共通に使用される値なので、後で他のスクリプトが変更している可能性があります。

mod_perlハンドラへのマッピング

<Directory "D:/WWWRoot/mod">
  <Files ~ "\.(cgi|pl)$">
    SetHandler perl-script
   ・
   ・
  </Files >
</Directory >

<Directory>とそれに続く<File>ブロックにマッチするファイルをperl-scriptハンドラにマッピングします。即ち、 D:/WWWRoot/mod下のPerlスクリプトファイル(.cgiと.pl) はmod_perlが提供するperl-scriptで処理されます。

Initハンドラの指定

PerlInitHandler Apache::Reload

mod_perlではクライアントからのHTTPリクエストを処理するために12種類のHTTPハンドラが定義されています。本編ではPerlInitHandlerPerlResponseHandlerを使います。PerlInitHandlerはHTTPレスポンスを生成するPerlResponseHandlerの前に実行されます。mod_perlの提供するHTTPハンドラについては以下を参照して下さい。

http://perl.apache.org/docs/2.0/user/handlers/http.html

ここではPerlInitHandlerとしてApache::Reloadを指定します。Apache::Reloadは%INCを使用してロード済みの全てのモジュールを調べ、変更のあったモジュールを自動的にリロードします。特定のモジュールだけをリロードの対象にする場合は、

PerlSetVar ReloadAll Off

を付け加えて、リロードしたいモジュールの中に以下のコードを加えます。

use Apache::Reload;

Apache::Reloadの機能は開発時に便利ですが、幾つかの注意点もあります。

Apache::Reload使用時の注意点

(1)package宣言の無いモジュールに関する問題

package宣言の無いモジュールを変更しApache::Reloadがモジュールをリロードした場合に問題は起こります。この時、モジュールはリロードされていないかのように見えますが、実際にはリロードされています。また、思わぬコンパイルエラーを発生する事もあります。この現象は、Apache::Reloadがモジュールをrequireする時、全てのグローバルシンボルがApache::Reloadの名前空間の中に入ってしまうために起こります。この時、正しい名前空間にあるのは古いバージョンのモジュールコードです。この様に、mod_perlを使用した開発ではpackage宣言の無いモジュールは混乱の原因になります。

(2)モジュール検索パス(@INC)の問題

Apache::Reload は%INCの他に@INCも使用します。@INCはサーバ起動時に凍結されるのでApache::Reloadからはスクリプトがコンパイル時に一時的に変更した@INCの値が分かりません。従って、Apache::Reloadは変更されたモジュールを検索する時やリロードする時に失敗する事があります。スクリプトの中で(use libなどにより)@INCを変更する事は開発時の混乱の原因になります。なるべくスタートアップファイルの中で@INCを設定する事を薦めます。

(3)コンパイル同期の問題

この問題はModPerl::Registryの場合に起こります。例えば、以下のようなスクリプトを考えます。

require MyModule;
MyModule::foo();

MyModule.pmが変更された場合、MyModule.pmはApache::Reloadによってリロードされますが、この事をスクリプトは知りません。なぜならスクリプトはコンパイルされていないからです。スクリプトが実行するMyModule::foo()は古いバージョンのままです。この問題を解決する一番早い方法は、スクリプトを再保存する事です。それ以外の解決方法としては、実行時にMyModule::foo()をスクリプトの名前空間にインポートし、foo()として呼び出す方法があります。

ModPerl::PerlRunの場合は、スクリプトは毎回コンパイルされるのでこの問題が発生しません。

以上、Apache::Reloadの使用について説明しましたが、詳細は以下のURLを参照して下さい。

http://perl.apache.org/docs/2.0/api/Apache/Reload.html

Responseハンドラの指定

PerlResponseHandler ModPerl::PerlRun

PerlResponseHandlerはHTTPレスポンスを生成します。このハンドラはmod_perlのHTTPハンドラの中でも中心的な存在です。このハンドラを自前で開発する事により、様々なアプリケーションサーバを構築する事ができます。本編ではmod_perlが提供する標準のレスポンスハンドラModPerl::PerlRunModPerl::Registryを指定しCGIをmod_perl環境下でエミュレーションする事を試みます。

mod_perl下では、Perlコードが一度ロードされコンパイルされると、それはメモリにキャシュされ、2度目以降はキャシュ内のコンパイル済みコードが再利用されます。ModPerl::PerlRunとModPerl::Registry の違いは「Perlコードのコンパイルのタイミング」の違いです。言い換えると「Perlコードのコンパイルの頻度」の違いです。ModPerl::PerlRun はCGIをより良くエミュレーションするために「コンパイル頻度をより多く」し、ModPerl::Registry の方はCGIをより早く実行するために「コンパイル頻度をより少なく」します。具体的に、いつコンパイルが実行されるのかは次章「mod_perlの実験;Perlのライフサイクル」を参照して下さい。

本編編集に当たって実験したところでは、ModPerl::PerlRunでもCGIに比べ十分に早い実行速度を得るとの確信を得ました。十分にモジュール化されたCGIでは両者の差は僅かだと思われます。なぜならpackage宣言を伴うモジュールの場合、コンパイルされるのは両者ともにロード時の1回だからです。ここでは、ModPerl::PerlRunを使用していますが、ModPerl::Registryに変更する場合は、以下のように変更します。

PerlResponseHandler ModPerl::Registry

オプションの設定

PerlOptions  +ParseHeaders

PerlOptionsディレクティブはPerlにいろいろなオプションを提供します。ParseHeadersオプションはスクリプトが以下のようなHTTPヘッダを送信する事を有効にします。

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

ParseHeadersの前の+は今のオプションにParseHeadersを加える事を意味します。



最終更新のRSS Last-modified: Wed, 29 Jun 2005 13:02:33 JST (4471d)