WebサーバとTomcatを連携させるプロトコル"AJP13"を、ちょっとした出来心からPerlで実装してみました。というわけでPerlでServletコンテナのフリをする方法。意外にパフォーマンスもよさげ。
AJP13とは
JavaServletコンテナであるJakarta TomcatとApache HTTP Serverを連携させる場合、Apacheにmod_jkモジュール等を組み込んでリクエストをTomcatに転送する方法が広く利用されています。mod_jkとTomcatの間では何種類かのプロトコルを選択し利用することができ、今回ネタにしたAJP13もその一つです。考え方は単純で、基本的には
- mod_jkはTomcatにSocketを貼る
- mod_jkはリクエスト情報をエンコードしてTomcatに送信する
- Tomcatは受け取ったリクエスト情報をデコードする
- Tomcatはリクエスト情報に基づいてServletを実行する
- Tomcatはレスポンス情報をエンコードしてmod_jkに送信する
- mod_jkは受け取ったレスポンス情報をデコードして、クライアントに送信する
要はmod_jk(Apache)がクライアント、Tomcatがサーバという構成で、SocketはApacheのプロセス毎にConnect/Close(Half-close?)しています。
プロトコル自体は非常にシンプルだし、Apache以外のIISやiPlanetとかと繋げられるし、mod_jk側で複数のTomcatを束ねてロードバランスもできたりと結構便利な仕組みなのですが、Java以外で利用されている例は多くないようです。って僕が知らないだけかもしれないので情報キボン。
PerlでTomcatのフリをする
というわけで、ここ2日間の暇な時間(何パーセントが暇なのかは秘密だ)をつかってAJP13のサーバをPerlで実装してみました。Net-AJP13-0.0.1.tar.gzトータルで700行程度の小さなコードです。あ、βはおろかα以前のテスト的な実装なので、生暖かく見守っていただけると吉。ちなみにこのバージョンでは
use threads;しているのでPerl 5.8.x系でthreadsが使える環境が必要です。
使い方はダウンロード後に解凍してmake; make installし、付属のajp13serverスクリプトを実行するだけです。
$ tar zxvf Net-AJP13-0.0.1.tar.gz $ cd Net-AJP13-0.0.1/ $ perl Makefile.PL $ make $ perl -Mblib bin/ajp13server終了はCTRL+C。デフォルトではlocalhostの8009/tcpでListenするので、必要であれば-pオプションでポート番号等を指定します。
$ perl -Mblib bin/ajp13server -p 8019あとは、Apache側にmod_jkモジュールを組み込んでおいて、普通にTomcatに繋ぐときのようにpropertiesファイルを用意・JkWorkersFileディレクティブで読み込んであげるだけです。httpd.confの例としてこんな感じ。
<IfModule mod_jk.c> JkWorkersFile /path/to/jk.properties JkMount /perl-servlet/* perlworker </IfModule>jk.propertiesファイルの例としてはこんな感じ
worker.list=perlworker worker.perlworker.port=8009 worker.perlworker.host=localhost worker.perlworker.type=ajp13設定が終わったらApacheを再起動して、ajp13serverも起動してみて設定したURLにアクセスしてみます。ちなみにmod_jkはリクエストが発生した時点でSocket貼りにいくし、失敗したら勝手にリトライするので、Apacheとajp13serverの起動の順番はあまり気にする必要は有りません。
$ curl -i http://localhost/perl-servlet/defaultモジュールの配布パッケージにサンプルとしておいてあるServletもどきは、webappディレクトリに置いてある2つだけ。
webapp/default.pm webapp/benchmark.pmコンテナ側でエラーレスポンスの処理をまったく実装していないので、受け付けられるリクエストはこの設定で
http://localhost/perl-servlet/default http://localhost/perl-servlet/benchmarkの2つだけです。ちなみにURIとclassのマッピング時に
my $class = $r->{uri}. '.pm'; $class =~ s{/perl-servlet/}{./webapp/};みたいなダサイことしているので、webappディレクトリと同じディレクトリでajp13serverスクリプトを起動する必要アリ。さらに上記の設定(JkMount /perl-servlet/*が重要)以外ではclassを見つけることができませんので注意。
制約事項と展望
ちなみにサーブレット等のAPIを決定するコンテナ部は差し替えられる作りなので、mod_perl互換やCGI互換のI/Fも作れるんだろうと思います。今実装してあるデフォルトのコンテナは超ダサイ実装。リクエストに応じてwebapp/*.pmをロードして、do_get()をコールしているだけ。全然Servletっぽくないので、便利に使うには少なくともあともう一枚レイヤーが必要なようです。ちなみにSession等の仕組みもまだ実装していません。
走らせてみると判ると思うのですが、意外に良いパフォーマンスを見せてくれます。簡単なベンチマークでTomcat 5 + mod_jkの約80〜90%程度のパフォーマンスです。もちろんいろんな部分が簡素(手抜き)なままなおかげかもしれませんが、まだまだ改善の余地はありそうです。
ほんとに真面目に取りかかるかどうかはさておき、ToDoをまとめてみる。
- Class Not Found etc...のエラーレスポンスの実装
- もっとマトモなクラスローダ + コンテナの実装(なんでWEB-INFやweb.xmlみたいなクソ面倒くさい手続きが必要なのかなんとなく判った気がする)
- mod_perl互換のコンテナの実装
- CGI互換のコンテナの実装
- 設定方法の決定(あたりまえですがXML禁止)
- リスナー部の不足機能の実装とリファクタリング
- パケット解析・組立処理のリファクタリング
- ドキュメント作成
- SourceForge.JPもしくはsourceforge.Netに置く
- 仲間募集(これ大事)
- プロジェクト名を決める(もっと大事)
そーいやP5EEってどーなったんだろう。