Java servletをPerl CGIに移植してみる

Javaのservletは、HTTPリクエストを受けてテキストを返す場合(HttpServlet)に限ると、役割はCGIと同じである。実行プロセスが常駐している以外は、CGIと大差が無い。CGIでも、例えばApache+mod_perlを使えばPerlのモジュールをapacheのプロセスに常駐させることができ、さらにJava servletと差が無くなる。従って、Java servletにすべきかCGIか、またCGIならどの言語を選ぶべきかは、動作速度と作り易さにかかっているのではないか…と思っていた。

先日、3択首都当てクイズのservletのコードを見直していて、文字列処理等でJava特有の面倒臭さがあり、PerlのCGIならかなり楽に書けるんじゃないか、と思った。
筆者はPerlは結構昔から使っているのだが、PerlのCGIを真面目に作ったことが無かったので、CGI.pmの勉強を兼ねて、3択首都当てクイズのサーバーサイドプログラムのCGI版を作ってみた。

CGIのソースコード
対応するクライアント(Java applet)側コード
対CGI版首都当て3択クイズの起動ページ

確かに幾分楽に書けたが、常駐型ではないことが前提になっている。当然、極端に遅い。速度を確保するには常駐型にする必要があるが、作ってみて思ったが、これを常駐型にするのは厄介である。
筆者はPerlのCGIを常駐型にする方法としてApache+mod_perlしか知らないので、これを前提に書くが、PerlのCGIはJavaと違って使用メモリ量を制限するのが容易ではない。このwebサーバーにもmod_perlを導入しているが、メモリに関しては結構苦労があった。適切なタイミングでガベッジコレクションさせる術が無いため、何回か毎にリセットするしか無いのである。1つのコンテキストで処理していると、リセット中のスループットが低下するため、2つのコンテキストで処理するようにしたいが、次にデータベースとの接続の管理方法が問題になる。もしTomcatのようなコネクションプールの仕組みが使えるとしても、そこまでしてマルチスレッドにしたいか?と悩んでしまう。
筆者はApache+mod_perlは重要な選択肢の1つだと考えているが、CGIをmod_perlでインターネットに公開するのは、怖くてできない。一般論として、CGIを常駐型で動かすのは、強引で裏技的な手段であり、好んで採用するものでは無いと思う。やはり、常駐型ならJavaのサーブレットで、非常駐ならCGIで作るのが良いのだろう、と考え直した。


以下、今回PerlのCGIを作るために学んだことをメモする。

●MySQLへのアクセスについて
PerlのDBIモジュールとDBD::mysqlモジュールをインストールして、man DBIやperldoc -m DBIで出てくるサンプルコードを参考にして使うと良い。
データの取り出しには、$dbh->select*や$dbh->fetch*というAPIがたくさんあって、どれを使うか迷うが、その都度考えるしか無いと思う。

●CGI.pmについて
基本的には、最初にCGI::new()してCGI::header()を出力すれば、後は自由にprintすれば良い。
クライアントからのCGIへのパラメーターは全てCGI::param()で取得できる。

●セッション管理について
標準的なモジュールはCGI::Sessionであり、それをより使い易くしたのがApache::Sessionだとされるが、Apache::Sessionにはセッションの有効期間を設定する術が無い。その為、今回はCGI::Sessionを使用した。
セッション情報の保存先をファイルでなくDBにする場合は、適切なドライバが必要になる。今回はMySQL DBに保存するためにCGI::Session::Driver::mysqlをインストールした。

CGI::header()を出力する代わりにCGI::Session::header()を出力すると、Cookieによるセッション管理のための情報が自動的に出力される。
Cookieを使ってセッション管理するなら、CGI::new()の戻り値をCGI::Session::new()の引数に入れる。Cookieを使わず、セッションIDを別にやり取りしてセッション管理するなら、セッションIDをCGI::Session::new()の引数に入れる。
Javaのservlet APIのHttpSessionクラスのencode()のようなURL rewritingのサポートは無いため、Cookieが無効でもセッション管理するなら、セッションIDをやり取りする方が良いと思う。

セッションパラメーターは、CGI::Session::param()を使って取得/格納できる。格納や削除をした後は、CGI::Session::flushをしないと、実際にファイルやDBには反映されない。

CGI::Session::Driver::mysqlを使う場合に必要なDBのアクセス権限は、SELECT,INSERT,UPDATE,DELETEの4つのようである。(manpageに明示的な記載は見つけられず、CGI/Session/Driver/mysql.pmとDBI.pmの中身から推測した)