mod_perlの手法

macopy氏:というわけで、mod_perlの話をします。mod_perlは「Apacheの中にPerlのインタプリタを組み込んでしまいましょう」という発想(のこと)ですね。

Apacheの中にもMPMという、どういうふうにプロセスを立ち上げておくか、リクエスト・レスポンスを処理するかを決めるモジュールがあります。それによってプロセスやマルチスレッドを処理したりするんですが、そのワーカーの中にmod_perlがロードされて、その中にPerlインタプリタが組み込まれています。

Perlプログラムを読んで、CGIを模したリクエスト・レスポンスをプログラムに与えてあげて、レスポンスを返す。これはもちろんCGI以外でも使えます。

Apache2とかにも関連しますが、1996年にmod_perl、2002年にmod_perl2(が出ました)。本来はApacheの機能拡張をPerlで行えるようにするモジュールですが、多くの場合はCGIプログラムをそのまま効率よく動かすために使われます。ModPerl::Registryとか、そういうHandlerを設定しておけば、CGIとの互換性を保ったまま高速化できるという売り込みだったんですね。

ただその反面、CGIの場合はプロセスがリクエストごとに終わっていたのが、もっと長くなってしまった。なので、昔のCGIプログラムはグローバル変数をかなり多用しているんですが、アプリケーションの状態をグローバル変数に保持してプログラムが書かれている場合に、CGIでは暗黙的に起動時に必ずグローバル変数が初期化されていたのが、mod_perlでは初期化がされないために動かなくなってしまうことがありました。

PerlのCGIプログラムをmod_perlで動かす

というわけで、PerlのCGIプログラムをmod_perlで動かします。hello.cgiをアップロードしましたが、先ほど言ったようにperlディレクトリの中でもmod_perlが動くようになっています。jcode.plですね(笑)。jcode.plも使っているから……。(画面を示して)こんな感じで、先ほどと同じものを映しました。同じようにパーミッションを変更します。

会場:なぜパーミッションを600ではなく644に変更する必要があるのか?

macopy:Apacheと上げているFTPのユーザーが違うからなんですよね……。

会場:了解。

macopy:補足ありがとうございます。.plでmod_perlのHandlerが動くようになっています。一応これも755でやっておきます。

その後にhello.plもちゃんと動くようになります。

ただ先ほど言ったように、普通のCGIと違ってmod_perlはプロセスが永続化されているので、リロードしても変わらないということがよくわかります。

じゃあスライドに戻ります。いろいろな視点があると思いますが、類似品としてはmod_mrubyだったりmod_luaだったりがあります。ほかに言語ごとに(も)あります。裏を返せばmod_perlはPerl専門家です。

同じようなものがApache以外にない(の)かというとあって、ngx_http_perl_moduleがあります。これはnginxでも同じようなことをするためのモジュールです。CGIとかはあまり関係がありません。

FastCGIの手法

次にFastCGIです。FastCGIが何かというと、これは先ほどのmod_perlと違って言語非依存です。ただFastCGIはmod_perlと同じで、プロセスが永続化されています。

じゃあ、その永続化されたプロセスとApacheのワーカー間での通信はどうなっているか。これは普通に標準入力と標準出力だったらいいけれど、立ち上げた時じゃないと環境変数はそのまま渡すことができないじゃないですか。

だからどうしているかというと、UNIXドメインソケットを使って、その中でバイナリプロトコルで通信されているHTTPのリクエスト・レスポンスがバイナリプロトコルに変換されて、FastCGIプロセスとApacheのワーカーが協調して動くかたちになっています。

これによってCGIのデメリットであった、プロセスが起動したり終了したりというコストが減って効率化できるし、FastCGIプロトコル自体がリクエストの多重化に対応しているので、スレッドを使ったり(できる)。例えばイベントドリブンという感じで、1つのプロセスが複数のHTTPリクエスト・レスポンスを扱える状況になっても対応できるのがメリットだったということですね。

というわけで1996年、奇しくもmod_perlとだいたい同じ(時期)。CGIプログラムの高速化は、けっこう明確に打ち出していました。一方で、先ほど言ったように(FastCGIは)言語非依存なんですが、弱点としては、完全なdrop-in replacement(になっています)。mod_perlみたいにCGIプログラムをmod_perlのほうにそのまま動かすのはできるんだけれど、FastCGIではそれはできなかったということですね。

ただ、セマンティクス。例えばさっき言ったクエリストリングだったりだけは引き継いでいる感じです。あと、先ほど言ったように多重化もできています。

FastCGI向けのプログラムを動かす

というわけで、FastCGI向けのプログラムを動かしていきます。fcgi/hello.fcgi、(FastCGIと)同じようなやつです。

ただこれはCGI.pmと互換のモジュール(である)、CGI::Fastを使っています。なので、生でsayとかをやっていたのよりはけっこう効率化されています。とりあえず1行空を出力することもないです。(画面を示して)こんな感じですね。

途中からEncode.pmを使っています。jcode.plだとちょっと面倒くさかったので(笑)。最後にpid=$$もやっています。こいつをそのままデプロイします。

デプロイはまたFileZillaでやります。fcgi-binにデプロイします。パーミッションの変更。

先ほどのjcode.plはもう使わなくなったのでアップロードしません。で、こんな感じです。

(会場拍手)

ありがとうございます(笑)。先ほどEncode.pmとか(が)使えるようになったので、こんな感じで……。どっかでミスったかな、文字化けしますね。mod_perlと同じで、これをリロードしてもpidは変わりません。こんな感じです。じゃあ戻ります。

実はSpeedyCGIというFastCGIの類似品もありました。(それは)PersistentPerlとも呼ばれます。これはCGIプログラムのshebangをPerlからSpeedyに変えるだけで、FastCGIみたいにプロセスの永続化をある程度してくれるやつです。

ただプロセスは永続化してPerlからはCGIと同じような感じで起動しますが、そこで小さいプログラムが起動して、もともと起動しておいたプロセスと入れ替えて実行することをやってくれます。でも、最近はあまり見ないですね。

PSGIの登場背景

というわけでPSGIの話をしていきます。ちょっと毛色が変わりますね。2009年にspecがCPANにshipitされたんですが、ただこの頃はWebフレームワーク群雄割拠時代で、CatalystとかCGIとかFastCGIとかmod_perlとか、それぞれで再実装をしていた時代になっていた。

そこで「フレームワークに依存せず、言語のシンプルなインターフェイスでそれぞれのHandlerを扱えるようにしましょう」という(ことで登場した)のがPSGIだったということです。なので、Plack::HandlerでCGIとかmod_perlの実装もあります。

ただ、この頃になってくると、Perlとかはランタイム自体がHTTPサーバー、HTTPをしゃべるようになってきたんですね。それまではあんまりしゃべるということはなかった。TomcatとかJavaとかはあったかもしれないけれど。そういうわけで、リバースプロキシという概念が一般になり始めたり、Apache以外の選択肢が出てきた。

じゃあPerlはどうしていたかというと、Plack::HandlerとしてStarmanとかStarletといったPrefork型のHTTPサーバーが実装されて、今となってはこれも現役で使われるようになりました。

じゃあPlack/PSGIは……。というより、HTTPサーバーをランタイムで立てる手法についての説明です。

Apacheはただただ、HTTPのプロトコルを後ろに受け流すリバースプロキシをするだけで、PerlではPrefork workerが動いていて、こいつらがマルチプロセスをうまく使って、HTTPリクエストを受けてレスポンスを返すというかたちになっていた。

生のPSGIを書く

というわけで、これはデプロイしませんが、生PSGI、つまり「Plackとかに頼らない生のPSGIはどんなものかを書いてみた」というものをサクッと説明しようかなと思っています。(画面を示して)こんな感じですね。

セマンティクスは、QUERY_STRINGみたいなのがCGIからもけっこう引き継がれているんですね。$envという引数を受け取る無名関数subがあって、この$envの中には、CGIの時代のものから引き継がれているPATH_INFOだったり、QUERY_STRINGといった環境変数がハッシュのキーとなって入っているということですね。

こんな感じでパースして$nameを取ってあげる。この$bodyというのは、PSGIの場合、最後に配列リファレンス3つで返すというルールがあるんですが、配列リファレンスの最後の要素に、配列リファレンスで囲って$bodyを入れてあげるということですね。あとヘッダーをcharset=utf-8で入れる。で、200という(結果が出るという)ような感じですね。じゃあ戻ります。

あとPSGIの類似品というかほかの言語ではどうしているかというと、一番古いのは、調べたところJava Servelet APIなのかなという感じです。その後にPython。これは発音があれなんですがWSGIとか、RubyだとRackとかがあって。

ちなみにおもしろいことに、例えばRackのspecの中にはWSGIに関する言及があったり、WSGIのspecの中であるPEP 333にもJava Servelet APIの言及があります。

PSGIとは何か。これはあまり調べていないんですが、JavaScriptのケースの場合はWSGIとRackとJSGIの言及があります。けっこうほかの言語から取り入れる時代になったのがこのあたりということですね。

あとPlackの類似品をインターフェイス視点で言うと、先祖としてはCGI.pmが最初にあり、CGI::ApplicationとかApach::Request。あと、直接的な先祖としてはHTTP:Engineだったり、それの参考になったCatalyst::Engineだったり。このあたりのモジュールの要素が組み合わさって、PSGIやPlackなどが構成されていると言えます。

(次回につづく)