xtetsuji) です。
おがた (@YAPC::Asia Tokyo 2013 トークなど編。その他の「YAPC::Asia Tokyo 2013」関連記事は目次ブログ記事からどうぞ。
いつもそうですが、今回もかなりの長文です(1万文字越え!)。mod_perlに特段の興味のない方は、流し読みでどうぞ!YAPC::Asia Tokyo 2013関連のブログ記事はこれが完結編の予定。
Contents
トーク「Apacheの展望とmod_perlの超絶技巧」について
昨年の初トークに続き、今年もYAPC::Asiaの壇上で2度目のトーク「Apacheの展望とmod_perlの超絶技巧」をさせていただきました。2年連続、今年も飽きずに得意の持ちネタであるmod_perlネタをぶつけましたが、今年も採用してくださって本当にありがたいです。世間では古い古いと邪険に扱われがちなmod_perlですが、こういうトークもソーシャルボタン等でそれなりの支持をいただいて嬉しい気持ちです。
最初はタイトルが「mod_perlの展望とApacheの超絶技巧」というタイトルだったのですが、スライドを作って発表が終わって一段落付くまで、Apacheとmod_perlというキーワードが入れ替わっていた事に気がつかないというボケをかましてしまいました。悩んだ挙句、トークページのタイトルをしれっと書き換えるという作戦に出ました。これくらいであれば許されますよね?
前のVimのトークはイベントホールが満席状態だったのに、自分が壇上に立ったら半分くらい人がいなくなったときは焦りました。ただ、イベントホールとメインホールは普段はそんな感じで、多目的教室が狭すぎたという意見も聞きますし、mod_perlというニッチな題材にしては、そこそこの入りだったかなと感じます。
実際、話したかった事は
- Apache httpd serverは、Nginx等の他のウェブサーバ実装の台頭でこの先どのようになるのか展望を話す
- mod_perl2 を使って任意のサーバが Apache2 と同等の堅牢性で書けるということのコンセプトを示す
というところだったので、タイトルとしては「Apacheの展望とmod_perlの超絶技巧」のほうが言いたかったことを表しているわけです。
前年は「mod_perlの本質とは何か」ということをmod_perl1とmod_perl2両方を例示して懇切丁寧に解説して大量のスライドが余った感じでしたが、今回はmod_perl1の説明は一切しませんでした。私自身が関わっている業務でもmod_perl1をmod_perl2にマイグレーションして、mod_perl1環境がほとんど残っていないですし(わざと自宅にmod_perl1化石環境を残しているだけ)、展望と超絶技巧を示すには当然mod_perl2だというのが背景にあります。
トークが採択されたのは「超絶技巧」という大げさな単語を入れたのも効果あったかなと感じています。「無駄にカッコイイ」などと褒め言葉もいくつか頂きましたし。日常生活では「超絶技巧」なんて言葉は使わないですよね。クラシック音楽ではフランツ・リストの「超絶技巧練習曲」という作品が有名で、そこから単語を採用させていただきました。
肝心のトーク内容については、無難にまとまったかなといった印象。前半の展望と後半の超絶技巧、両方そこそこだったかなと。もっとうまくできれば…という点はもちろん色々あるんですが、終わってから欲を言っても仕方が無いわけで。
中間部にあのトークを意識した「もう一つの本当にあったレガシーな話」というスライド群を入れたのですが、時間の関係で飛ばしてしまいました。内容はスライドままなので、興味のある方は読んでみてください。
mod_perl2でmemcachedサーバを実装するというコンセプトが当日までに完成しなかったのですが、完成したところで20分のトーク中に実演することもなかったので、これからに期待してくださると幸いです。特にこの事例は実用を目指しているというわけでなく、コンセプトとしてこんなことも出来るということを示したいという意図程度です。
mod_perl1はHTTPリクエスト処理しか書くことができませんでしたが、mod_perl2ではTCPコネクション層から処理を書くことができます。これはApache1.3とApache2.xのモジュールAPIと対応しています。Apache1.3では本体にパッチを当てないとSSL接続を直接受けられなかったのが、Apache2.0でmod_sslが生まれて単体で処理が可能となった事などが好例です。
世間は新しいものが受容される環境ばかりではありません。古式ゆかしい納品型受託案件などでAnyEventはまだ新しい実績の乏しいものと見られ敬遠されるケースがあるかもしれません。daemontoolsはdjbアレルギーで拒絶される場面もあるかもしれません。そんな時にApache2/mod_perl2で任意のTCPサーバが書けることがプロジェクトの突破口になることもありうるのでは、という情報を提供したかったということもあります。現にqpsmtpdはApacheをEngineとして動作するmod_perl2モジュールが存在しますし、mod_mrubyやmod_luaなど、新たなApache拡張系の登場も、このジャンルがまだ現役である事の証拠だと思います。先輩であるmod_perlがmod_mrubyやmod_luaへの実用例の開拓者になるって素晴らしいと思いませんか?
トークでお話した通りですが、展望面では、Apache2.6では大きな変更は無いとしても、Apache3.0が依然として謎のベールに包まれています。またApache2.4でのmod_perl2対応がWindows環境で難航していることで今後どうなるのか、見守りたいです。未成熟なevent MPMが今後成熟してNginx等と切磋琢磨していくのかなど、今後もApache/mod_perlから目が離せません。随時ウォッチして、ブログやトークなどでアウトプットしていきければと思います。
会場では @tokuhirom さんが聴講してくださって質問やアドバイスをいただけたのは嬉しかったです。「Apache2をAnyEventのエンジンにするのは?」は、実用とは別に興味深い課題。つい「そのアイデア、頂きました」と言ってしまいました。
後日 @tokuhirom さんが書いたmod_perlに関するブログ記事もリンクしておきます。
「本当にあったレガシーな話」を聴講して
いやはや、1日目に20分mod_perl推し(?)トークをした私でしたが、2日目の @lestrrat さんの「本当にあったレガシーな話」はまさに40分mod_perl dis吹き荒れる真逆のようなトークで、聴講していた私も内心ビクビクしつつ、楽しんで聴講しました。
[tweet https://twitter.com/xtetsuji/status/381289572115562497]さて、会場で爆笑していた人の中で、あまりmod_perlに詳しくない方のために、私なりの解説をしてみようと思います。
その前に注意点として、私はこの内部コードの事を当然知らないわけで、憶測や推測で語っている立場だということ。また、私自身の無知や無理解による誤解や間違いが含まれている可能性があるということ。これらをご留意下さい。
前提として、@lestrrat さんの「脱mod_perl、PSGI化推進」という方向性は2013年の今の選択として正しいと私も考えています。一度PSGI化してしまえば、Plack::Handler::Apache1を使って「裏返し」にmod_perl1を据えることさえ可能だからです(はたしてそれをしたいかは別として)。Plack::Handler::* の数だけ裏側のウェブサーバの実装とPerlの永続化手法も選び放題です。このメリットは計り知れません。その他に得られるメリットも枚挙にいとまがない。
このトークの「mod_perlをPSGIに書き換える一大事業」は大きく分けて二部あって、前半が「mod_perl1ハンドラ(sub handler)で書かれたアプリケーション」、後半が「mod_perl1に密に依存したSledge」でしょう。mod_perl1の問題点は前半で大体語りつくされており、後半も色々な試行錯誤があるものの、前半の部分に話の比重が置かれてか、軽めな印象を覚えました。
前半のmod_perl1ハンドラで書かれたアプリケーションの話。Apache::Request->instance と言われて一瞬意味がわからなかったのですが、libapreq のことだったんですね。
mod_perl1で「リクエストオブジェクト ($r)」と呼ばれるものは主に2つあって、
- Apache オブジェクト (ref $r eq ‘Apache’)
- Apache::Request オブジェクト (ref $r eq ‘Apache::Request’) a.k.a. libapreq
というものがあります。今回 @lestrrat さんが語っていたのは後者の Apache::Request のほう。
「もともとmod_perlはApacheのモジュールをC言語ではなくPerlを使って書けるようにしたもの」というコンセプトに立ち返ってC言語でのApacheモジュールの開発に降りて考えると、mod_perl1でいうApache オブジェクトが、Apache1.3 での mod_*.c 内での原始的なリクエスト構造体 (request_rec 構造体) の対応です。ただ、この request_rec 構造体はHTTPリクエストを扱うにはあまりにも貧弱で、例えばファイルアップロード(multipart/form-data)のPOSTデータを受け取ってそれをほどくことすら自前ではできません。C言語を書くプログラマにとってこれはあまりに苦痛でしかないことは容易に推測できるわけで、これを補うために libapreq というライブラリが Apache側から提供されました。これを使えば大体のHTTPリクエスト処理ができるようになります。このlibapreqのPerlバインディング(XS)がApache::Requestと理解していただいて良いでしょう。
資料には Apache->uplaod() で Apache オブジェクト (request_rec 構造体) から upload オブジェクトを取得できるとされていましたが、upload オブジェクト (Apache::Uploadオブジェクト) を取得するためには確か libapreq / Apache::Request の力が必要なはずです。ここでは「Apacheオブジェクトは貧弱、Apache::Request (libapreq) オブジェクトは強力」ということを覚えておいてください。というか、libapreq はあまりにも強力なものです。良くも悪くも。
実は私は、ジョークやコンセプト以上の目的で libapreq / Apache::Request を使ったことがありませんでした。なので Apache::Request->instance という文字列が出てきて理解するまで10秒くらいかかったのはここだけの話。この呪文 Apache::Request->instance は、どこでも都合よく強力なApache::Requestリクエストオブジェクトを虚空から取り出せる呪文のようなもの。1つのリクエストサイクル中でリクエストオブジェクトは実質的にシングルトンであるとはいえ、このクラスメソッドを使ってしまうことで、コードの量が多くなればなるほど可読性は大いに下がると推測されます。
Perl CGIのRegistry高速化は別として、ハンドラとしてのmod_perl はあくまでも「複雑なことをさせない」「外部との密結合を避ける」ことが大事だというのが私のモットーだったので、libapreq / Apache::Request を使った時点でそのモットーが崩れるというのが私の意見。要するに libapreq / Apache::Request を使った時点で、複雑なウェブアプリケーションをmod_perlハンドラで書くという船出をしてしまったようなものと言えるかもしれません。
「単純なことをする」「疎結合である」という要件が満たされるのであれば、速度や可搬性を持った良いmod_perlハンドラアプリケーションが書けるでしょう。例えば簡潔なJSONを出力するAPIなどのようなもの。HTMLの画面を出力するサーバとは分離したApache/mod_perl世界があるときなど、こういう手法は悪くないと思います。将来の移植性に置いても心配はそれほど無いはず。
トークでは「$rは多機能過ぎる」という下りがありましたが、まさに$rはmod_perl1のオブジェクトという存在以上に「Apache1.3そのもの」であると言っても過言ではないでしょう。この「多機能」というキーワードには、モダンなフレームワークが持つコンテキストオブジェクトのような移譲による役割分担をせずに、$rだけであらゆることをやろうとするという雑然としたメソッドの世界観を含んでいると感じました。また「ブラウザからのHTTP接続の切断」といった状況すら検知できる(皆さんがお使いのHTTPサーバやフレームワークではできますか?)という意味での強力さに手を出してしまうと移植性が途端に低下してしまい、Apacheロックオンの危険性を感じる立場もあるでしょう。
mod_perl1ハンドラは、いわばC言語を使わずPerlを使って書かれたApache1モジュールそのもの。サイトexample.jpの画面とロジックを作るとき、mod_examplejp.cを作ろうと普通の人は考えないのと同じ理由で、mod_perlで画面とロジックを書く事は迷宮への入口を思わせます。HTMLの画面としてのレスポンス結果を作成するのは、mod_statusくらいのHTTP GET単発画面を出す程度の簡単さがギリギリラインかなと思います。そうでなければ移植性を考えてPerl CGIを書いた上でmod_perl高速化環境を使うか、この層を抽象化したフレームワークを使いたいところです。
後半のSledgeをPSGI化する話。実際にSledgeを使ったアプリケーションを書いたことは無かったのですが、私が当時mod_perlハンドラを学び始めた頃の活きた教材がSledgeでした。
標準のSledgeはPerlの永続プロセス手法としてmod_perl1(2ではない)以外のバックエンドを持っていない事がまず問題です(CGIは非永続プロセス手法なので考慮外)。SledgeをPSGI化する外部の試みのいくつかも、大規模サイトに投入するにはネックとなることがトークで語られていました。
余談ですが、SledgeのAPI自体も非常にmod_perl1の影響を受けているようで、コンテキストオブジェクト (という名前が正しいのかな) のメソッド名はmod_perl1のリクエストオブジェクトのメソッド名を築一参考したと容易に推測可能なものです。これは、当時よくHTTPリクエストを抽象化していたものがCGI.pm以上にmod_perlのリクエストオブジェクトだったから、それ以外の実装がなかったから、という理由なのでしょう。当時「HTTPリクエストの抽象化」を意識できる教材は、SledgeかJavaのTomcat、Strutsくらいしか無かったような気がします。このような先人の苦労が今のPlack::Requestに結実していくのです。
トークでも語られましたが、Sledgeのmod_perl1部分はApacheオブジェクトは当然として、なんとそれの進化系であるApache::Requestに完全に依存しています。まずフェイクオブジェクト(のようなもの)を作成するという @lestrrat さんの作戦は、問題解決への鋭いアプローチだと感じました。
$r->status(200) + return 404 のくだりは私も理解が足りない部分だったのですが、エラー時にmod_perl1がヘッダ類を出力するときの癖というのは結構あります。Apache1.3コアのErrroDocumentディレクティブもそうですが、エラー画面を出力するという処理には内部リダイレクトという処理が関わってきて、それがApache1.3/mod_perl1上でのヘッダ処理をややこしくしているのではないかと感じています。私の定石は「$r->status()は使わない」「$r->send_http_headerをしたあとでステータスコードをreturnする」です。確かに $r->status() を使うと変な事が起こりがちかも。
Plack化の最初でパフォーマンスが落ちたという話。Plack::Request中でクエリ引数処理が重かったとのこと。Plack::Requestのクエリ引数のパース処理はPure Perlだったと思いますが、mod_perl1は古ぼけていていもクエリ引数のパースはmod_perl1のネイティブAPIがやるので爆速なはずです(libapreqを使う使わないに関わらず、内部的にはC言語での実装です)。
トークで語られたような当時のPlack::Request中にあった重複処理などを改善していく話はエキサイティングでしたね。そもそも、Plackの目的がパフォーマンスよりもPSGIのリファレンス実装やPlack::Handler::*によるウェブサーバの差異の吸収といった部分を優先している(ように見える)ことからも、見逃されていた重複処理があっても仕方が無いかなという気はします。クエリ引数のパース処理が目に見えてパフォーマンスにおいて支配的になるのは大規模サイトだということも、今まで見逃されてきた(?)一因でしょう。こういう観点からも、Plackの大規模サイトへの投入は興味深い事例だと感じました。
ここまで長々と私の解説を書いてみましたが、誤解や間違いがありましたらご指摘下さい。
両方のトークを比較する
長文での説明でしたが、私のトークと @lestrrat さんのトーク、対立するものではなく、向いている方向が違うという感じなんですよね。
- @xtetsuji のトークは最新のPerlとmod_perl2の先端を使い尽くす
- @lestrrat さんのトークは太古のPerlとmod_perl1の呪縛から解放する
そういう意味では、両方のトークを聴講した方が、mod_perl というものをどう受け取ったのか、興味深い部分ではあります。
聴講中に
[tweet http://twitter.com/xtetsuji/status/381306340414476288]なんてツイートをいただいてどうしようかと思いましたが、時間切れで質問タイムはナシ。でも聴講時の私の認識は今よりも甘く、結果的に質問しなくて良かったかなと感じています。
@lestrrat さんからは
[tweet http://twitter.com/xtetsuji/status/381297867916185600]と返していただいて微笑ましい感じでした。トーク後に10秒ほど会話をさせていただきましたが、両者の考えが互いに良い意味で伝わったような気がします。対立関係じゃないですよ!(笑)
今もmod_perlを採用している人達からの声とそれへの返答
その後、後夜祭などで声をかけてくださった方で、今も業務でmod_perl1アプリケーションを動かし続けている方が結構いることに驚きました。「動くものは触らず」の原則はあるでしょう。それについては否定できません。ただ、古いものを使い続けることへのある種の漠然とした不安を持っているという印象を覚えました。今回のレガシーな話のトークの影響も少なからずありそうです。
私が業務でmod_perl1からmod_perl2へマイグレーション作業をすることになったのは、Ops側が最近掲げた「Debian stable付属パッケージを全ての拠り所にする」というポリシーが大きいです(他にも細かな理由はありました)。Debian stableは既にApache1.3/mod_perl1を外してしまったため、マイグレーションの必要性があったわけです。私はDebian原理主義者的な部分があるほどのDebian好きなので、このポリシーには歓迎をしていて、ブランチを切って一気にマイグレーション作業を完結させました。
ただ、mod_perl1/2の知見が少ない企業が、今も引き継がれ残るmod_perl1アプリケーションをmod_perl2にマイグレーションすることは現実的ではないと感じます。mod_perl1→mod_perl2はApacheのモジュールAPIに対応するように、ある種のAPIの断絶があります。作業としては楽なものではなく、Plack等の別の基盤に載せ替える作業と大して変わらない作業量かもしれません。そうであればPSGI化するほうが良いに決まっていることは前段で述べた通り。
私、ネタとして「mod_perl芸人」的な部分を狙っていると言われればそうなのですが、ビジネス上大事な場面や商用環境でまで何でもmod_perlという立場ではありません。何でも適材適所というのは上述でも繰り返していること。むしろ、最近は業務でも個人でも、ネタ以外でmod_perlプログラムあまり書かず、AnyEventやMojoliciousを使うことが多いんですよね。まぁ、デプロイは慣れたApache2とmod_perl2(Plack::Handler::Apache2)を使って…ということはありますが、MonocerosのコードリーディングをしたりNginxのHttpPerlModuleを調べたり、そういう方向への研鑽も続けています。
ただ、せっかく得たmod_perlの知識。mod_perlを何かにマイグレーションしたい方や、mod_perlアプリケーションを何らかの理由で保守したり新規開発したりといった方へのアドバイス等に活用していきたいと考えています。@xtetsuji や @mod_perl_info にお気軽にご相談ください。
これからどういう活動をするの?
嬉しいことに「mod_perl芸人」はそこそこウケがいいので、小さな場では時々ネタを披露しようと思います。あと前述のようにレガシーに悩んでいる方のために、Apache/mod_perlのウォッチ活動は続けていきたいと思っています。リクエストがあれば、トークやマイグレーション等のアドバイスなどの活動は積極的に行っていきます。ブログなどのネット上でも細々と活動していくつもりです。
私は「新しいもの=善、古いもの=悪」という図式には完全には賛同できない部分があります。初学者の「CGIでゴメンナサイ」発言だなんて「大規模になって立ち行かなくなったときに改めて考えればいいのであって、まず動くものが作れる事がまず大事ですよ」「フレームワークは余裕があれば」と声をかけることもしばしば。それとは同時に、Perlの歴史が長いことで、古い真の意味で廃れた情報が未だに現役の如くネット上に残り続ける負の側面も知っています。何が良くて何が悪いのか、何が正しくて何が正しくないのか、そういう部分も意識して、誤解がないように各スキル層へのアプローチをしていきたいと考えています。
[tweet https://twitter.com/xtetsuji/status/395061721900920832]Mojolicious飲み会等、新しい試みもしています。私の新しい側面にもご期待いただければと思います。
「来年のYAPC」(期待)のような大きな場での発表は、特段リクエストがなければmod_perlトークはしないかなと思っています。さすがに3度目は無いかなぁと。MPMは別として、APIとしてのApache2は既に枯れており、2回のYAPC壇上で話した内容が大枠の説明を網羅していることでしょう。
今年の YAPC::Asia Tokyo 2013 でも様々なジャンルのトークが行われました。Perlというプログラム言語の可能性は広く、これからも広がり続けることでしょう。そのための力となるべく、様々な事に取り組んで、微力ながらPerlというプログラム言語でありコミュニティを盛り上げていきたいというのが、私の今後の活動だと認識しています。
これからも精力的に活動していきますので、どうぞよろしくお願いします!