2.2K Views
February 24, 24
スライド概要
2011/07/16に Hokkaido.pm#5 で発表したスライドです。
I like 🚌 and ☕
mod_perl温故知新 〜Perl CGIの高速化から メールサーバまで〜 株式会社fonfun 尾形 鉄次 (OGATA Tetsuji) Twitter: @xtetsuji 2011/7/16
自己紹介など
自己紹介(1/2) • 尾形 鉄次 (OGATA Tetsuji) Twitter: @xtetsuji • http://post.tetsuji.jp/ (最近ブログ始めました) ここで語りきれないことも書きたいです • 北海道の音更町(帯広市の隣町)出身です。 高校卒業までの18年間を北海道で過ごしま した
自己紹介(2/2) • 人生初スピーカー (お手柔らかに…) • (ポスト)モダンPerlに乗れていない30代 • JavaScript / Shell Script(bash) // Emacs Lisp • 趣味: クラシック音楽、カフェ散策、 路線バス
所属紹介(1/2) • 株式会社fonfun(フォンファン) http://www.fonfun.co.jp/ • 主力製品:リモートメール http://rmail.jp/ • 任意団体g15アソシエーション http://g15.jp/
所属紹介(2/2) • 株式会社fonfun(旧社名:ネットビレッジ) • 1999年からウェブメール (商品名:リモートメール)を運用 • 2003年に第4世代システム(Perl + Apache/ mod_perl + Oracle + Post x)を開発 fi • 最近新しい技術的な試みにチャレンジ中
今回お話をさせていただく ことになった経緯 • 地元にスカイアークシステムという会社がある • YAPC::Asia2010でスポンサーをしているのを発見 • 興味があると言っていたら、とある人が中の人 である永谷さん(@onagatani)を紹介してくれた • しかも Hokkaido.pm の偉い人だった! • いまここ
Agenda
Agenda • I. mod_perlを振り返ってみませんか? • II. mod_perlの真髄はApacheの拡張 • III. Apache2&mod_perl2でHTTPの外へ • IV. 怠惰と応用 - qpsmtpd & nginx... • まとめ
I. mod_perlを 振り返ってみませんか?
いまさら Apache+mod_perl? • 【質問】WAF、PSGI/Plackを使えば、 ※1 Apacheなど意識しなくていいのでは? ※1. WAF=Web Application Framework • 【回答】確かにそうかもしれません。 だけど、知識として知っておいても損は 無いはずです • 例えばPlack+mod_perl2でも、変なバグ を踏んでしまうこともあるようです ※ http://subtech.g.hatena.ne.jp/cho45/20101221/1292941055
いまさら Apache+mod_perl? • Apache+mod_perl+WAFでもmod_perlの 内側を多少知っておけば… • バグを踏んだ時、役立つ • パフォーマンス改善のヒントがある
いまさら Apache+mod_perl? • 今もApacheは最も使われているサーバ • どのLinuxディストリビューションにも 大抵Apacheとmod_perlのパッケージは 標準で用意されている(はず)
いまさら Apache+mod_perl? • 今もApacheは最も 使われているサーバ • Apacheのシェアは 2011年7月現在65.86% (2011年6月だと64.88%) (http://news.netcraft.com/ archives/2011/07/08/ july-2011-web-serversurvey.html より) • いつの世も半数以上Apache Market Share for Top Servers Across All Domains August 1995 - June 2011
いまさら Apache+mod_perl? • いつでもゼロから最新のWAFを使って 開発ができるとは限らない • 受託案件やコンテンツ移管案件など、 レガシーなApacheとPerl CGI(or PHP)の 製品を渡されることもある大人の世界 • リファクタリング、 マイグレーション...
mod_perlのバージョン • Apache1.3 → mod_perl1 • Apache2.x → mod_perl2 • 1と2ではmod_perlの文法の違いは大きい • 歴史の狭間のmod_perl1.99(要注意) • ※現在Apache1はdeprecatedだが、世間では現役と言えるので、mod_perl1 についても、なるべく触れます
mod_perlの歴史 • mod_perl1 rst public release: 1996/3/25 • mod_perl2 rst public release: 2002/4/6 (http://perl.apache.org/dist/mod_perl-1.0-current/Changes より) fi fi (http://perl.apache.org/dist/mod_perl-2.0-current/Changes より)
Perl CGI の高速化 # ↓Apache Con gurations • mod_perl1 AddHandler perl-script .pl PerlHandler Apache::Registry AddHandler perl-script .pl mod_perl2 PerlResponseHandler \ ModPerl::Registry • • こう書くことで、拡張子”.pl”のPerl CGI fi が高速化する
例えば hello.pl
mod_perl1でHello1.pm • 「レスポンスハンドラ」を書く • レスポンスハンドラとは、第一引数にリク エストオブジェクト($r)を受け取る handler サブルーチンが定義されたパッケージ • http://hello.example.jp/ 以下にアクセスした ら、遍く Content-Type: text/plain で定形の挨 拶するだけのサンプルを作ってみる
mod_perl1でHello1.pm fi # apache1.3 con guration <VirtualHost A.B.C.D:80> ServerName hello.example.jp SetHandler perl-script PerlHandler Hello1 </VirtualHost>
mod_perl1でHello1.pm
mod_perl2でHello2.pm • 同様のサンプルを作成 • 基本はmod_perl1と同じ • 文法は結構違います
mod_perl2でHello2.pm fi # apache2.x con guration <VirtualHost A.B.C.D:80> ServerName hello.example.jp SetHandler perl-script PerlResponseHandler Hello2 </VirtualHost>
mod_perl2でHello2.pm • PerlHandler → PerlResponseHandler 名前がようやく明快になった • ただ Hello2.pm のコードは結構違います
mod_perl2でHello2.pm
mod_perl2でHello2.pm • mod_perl1はmod_perlのパッケージ名前 空間は Apache:: のみ • mod_perl2はmod_perlのパッケージ名前 空間が Apache2::、ModPerl::、APR:: 等と 細分化され、mod_perl1の巨大なクラス (Apache object等)もMix-in的に分割された
Helloと挨拶しても... • 今までやってきたのはCGIやWAFでも できる簡単HTTPレスポンスを返す処理 • ようやく本題へ...
II. mod_perlの真髄は Apacheの拡張
mod_perlの真髄は Apacheの拡張 • 今まで見てきたのは、HTTPレスポンス を返す処理 • mod_perlはHTTPレスポンスを返すだけ でなく、その前処理や後処理もできる
Apacheの内部処理 • MPMは多く使われているPreforkを仮定 • 親プロセスを起動すると、設定に従っ て子プロセスを設定の数だけ起動 (prefork) • 各子プロセスはHTTPリクエストを待つ • では子プロセスごとのHTTP処理は...
Apacheの内部処理 • リクエストを受ける • ヘッダを解析する • 必要に応じてURLを変換したり、DocumentRootを手がかりに実パス を割り出す • アクセス制御、認証、承認(BASIC認証など) • MIMEタイプを考える • レスポンスを出す(静的ファイル、プログラム出力) • ログ(アクセスログ、エラーログ)を出力する • 次のリクエストを待つ
Apacheの内部処理 • ざっくり列挙しただけなのにビッシリ • 各処理を行っている部分にはフックがあって、 ここに各種処理を挟むことが可能 →Apache Module(mod_xxxxxx) • Apache Module でできることを Perl ででき るようにしたものがmod_perl • これがmod_perlの真髄です
Apache/mod_perl 処理フェーズ • 各フックがある部分: 「(処理)フェーズ」 • リクエスト待ち状態から各フェーズを一 巡するので、全体を「リクエストサイク ル」と呼びます • Apache/mod_perlの1と2で結構違います • Apache/mod_perlの2のほうが高機能
処理フェーズ:mod_perl1 PerlChildInitHandler PerlPostReadRequestHandler PerlInitHandler PerlTransHandler PerlHeaderParserHandler PerlAccessHandler PerlAuthenHandler PerlAuthzHandler PerlTypeHandler PerlFixupHandler PerlFixupHandler PerlHandler PerlLogHandler PerlCleanupHandler PerlChildExitHandler ※一部省略があります。詳しくは以下を参照 fi http://perl.apache.org/docs/1.0/guide/con g.html#toc_Perl_Handlers
処理フェーズ:mod_perl1 ※「Practical mod_perl」より抜粋
処理フェーズ:mod_perl2 PerlChildInitHandler PerlPostReadRequestHandler PerlInitHandler PerlTransHandler PerlMapToStorageHandler PerlHeaderParserHandler PerlAccessHandler PerlAuthenHandler PerlAuthzHandler PerlTypeHandler PerlFixupHandler PerlFixupHandler PerlResponseHandler PerlLogHandler PerlCleanupHandler PerlChildExitHandler ※かなり省略があります。いくつかは後ほど。または以下を参照 fi fi http://perl.apache.org/docs/2.0/user/con g/con g.html
処理フェーズ:mod_perl2 ※「Practical mod_perl」より抜粋
実例 • 時間の都合上、多くをご紹介できませ んが、少し実例を見てみましょう
実例: mod_rewriteの リファクタリング • よく使われているご存知mod_rewrite • 単純なURL正規表現置換に便利 • ただ、処理行数が多くなってきたり、 (頑張れば書けるが)条件分岐や反復処理 に手を出すと途端に黒魔術化
実例: mod_rewriteの リファクタリング • mod_rewrite処理を、URLを書き換える フェーズのPerlTransHandlerで実装する • (使うなら)条件分岐や反復処理がPerlの コードになるので可読性がよくなる • mod_rewrite自身PerlTransHandlerに対応 するC APIのフェーズで動作しています ※実際はPerlFixupHandlerに対応するフェーズでもお仕事をしています
実例: mod_rewriteの リファクタリング # required “RewriteURL.pm” <VirtualHost A.B.C.D:80> ServerName hello.example.jp PerlTransHandler RewriteURL </VirtualHost>
実例: mod_rewriteの リファクタリング
実例: mod_rewriteの リファクタリング • 「レスポンスハンドラ」と同じような 文字通り「ハンドラ」を書きます • sub handler { my $r = shift; ... } • 他のフェーズでもこの書き出しは定石
他にできること一例 • 各フェーズでこんなことができるよ! という概念だけご紹介します。 • 実際の体験談を多く盛りこんでみます
他にできること一例 • PerlPostReadRequestHandler 取り急ぎ悪質なDDoSを遮断(応急措置) • return DONE; # cut off! • 本来はPerlAccessHandlerでするのが良い • 最終的にはLBやiptablesで遮断かな
他にできること一例 • PerlTransHandlerは本当に役立ちます • mod_rewriteがスイス製アーミーナイフ なら、PerlTransHandlerはプロの料理人 が使う高級包丁?それとも日本刀? • URL書き換えだけでなくリダイレクトも 当然できます(return REDIRECT; など)
他にできること一例 • Perl{Authen,Authz}Handler シングルサインオン実装 PHPでもレスポンス処理に入る前に Cookie処理などをPerlで行える • Access,Authen,Authzの3種は3Aとも呼ば れる。アクセス制御、認証、許可。
他にできること一例 • 既に大量のHTMLファイルのある静的サイトを受 け取ったプログラマの孤軍奮闘 • 依頼「User-Agentを見て *.html の Content-Type を text/html か application/xhtml+xml か、出し分けして いただけますか?」 • PerlTypeHandler か PerlFixupHandler でやりましょう (入出力ヘッダ処理: $r->headers_in, $r->headers_out)
他にできること一例 • 依頼「アフィリエイトで全てのHTML ファイルの</body>の前に動的にコード を入れていただき(ry」 • PerlHandler / PerlResponseHandler を使え ばお手軽に解決 fi • $r->print(thiswork(slurp($r-> lename())));
他にできること一例 • 依頼「iモードHTMLで書かれた絵文字 入り静的HTMLが大量にあるんですが、 SBM端末が来たら変換(ry」 • これも PerlHandler / PerlResponseHandler でお手軽かつ透過 的に解決しましょう
他にできること一例 社内コード Kepler/Apache/ConvertEmoji.pm より抜粋
他にできること一例 • HTTPレスポンス出力処理 / PerlHandler / PerlResponseHandler が完了した後... • PerlLogHandlerは$rからHTTPレスポンス 結果や送信サイズ数が取得可能な状態 • 様々なDBにログを書き出したり通知を したりできます
他にできること一例 • 他にも本当に色々な使い方ができる mod_perlのHTTP処理ですが、そろそろ 次の話に移ります
III. Apache2&mod_perl2 でHTTPの外へ
Apache2&mod_perl2で HTTPの外へ • 先ほど紹介したmod_perlの処理フェー ズはHTTP Request/Responseのお話 • $r : Apache (mod_perl1) • $r : Apache2::RequestRec (mod_perl2) • 慣習名 $r の r は request/response の r
Apache2/mod_perl2 での新設サイクル • Apache2にはHTTPを扱う以外のサイクルもある • ServerLifeCycle 関連 • Protocol / Connection 関連 • Filter 関連 • Thread 関連 • 当然mod_perl2でも、それを使ってHTTP以外の処 理が書ける fi fi ※ http://perl.apache.org/docs/2.0/user/con g/con g.html を参照
Apache2/mod_perl2 Connection サイクル • 今回はConnectionサイクルに注目 • (HTTPではない)Protocolサイクルとも • ConnectionサイクルはHTTPを扱う サイクル・フェーズよりも前に位置する • Apache2での応用例: mod_ssl
Connection サイクルの 処理フェーズ PerlPreConnectionHandler PerlProcessConnectionHandler ※「Practical mod_perl」より抜粋
Connection サイクルの 処理フェーズ • 2つだけ。HTTPよりもシンプル? • PerlPreConnectionHandler • Apache処理の本当に冒頭に位置する • PerlProcessConnectionHandler • 今回ここにSMTPサーバを作ります
Apache2 SMTPサーバの 作成動機 • 開発内容はメーリングリストサービス • Post x pipeを使えばPerlプログラムでI/O 処理が書けるが、forkのコストが高い fi • 重いDB処理、絵文字処理がしたかった
Apache2 SMTPサーバの構 成 • ハードウェア構成: 2台(load balancing) • 外部との送受信SMTPサーバは、既に 稼働中の別のハードウェア数十台程 度のクラスタがある • 「Apache2 SMTPサーバ」の内部では、 fi Post xに受信と送信の処理を依頼...
Apache2 SMTPサーバの構 成 • 矢印はSMTPの流れ • サーバ内で両端でPost xを構え させているのは、煩雑なキュー の管理を省きたい思惑がある • Apache2 SMTP サーバが1つの Post xから見て別ポートで動作 fi fi しているコンテンツフィルタの ように稼働する
mod_perl2で PerlProcessConnectionHandler • 「コネクションハンドラ」を書く • 今までの$rと違い$c (Apache2::Connection) オブジェクトを第一引数に受け取る • sub handler { my $c = shift; ... } • Apacheの設定 • 実際の例を紹介しますが、長いので抜粋
PerlProcessConnectionHandler で書いたSMTPサーバの詳細 fi fi fi fi Listen 10025 gm lter AcceptFilter gm lter none <VirtualHost 127.0.0.1:10025> TransferLog /var/log/httpd/gm lter.log ErrorLog /var/log/httpd/gm lter_error.log PerlModule Encode::NV \ ARM::G4::Apache2::GMFilter \ Apache::DBI PerlProcessConnectionHandler \ ARM::G4::Apache2::GMFilter </VirtualHost>
PerlProcessConnectionHandler で書いたSMTPサーバの詳細 社内コード ARM/G4/Apache2/GMFilter.pm より抜粋1:冒頭部分
PerlProcessConnectionHandler で書いたSMTPサーバの詳細 • ポイント:重たいモジュールを大量に useしても大丈夫! • $cのために use Apache2::Connection • Socket I/O処理のためにAPR::Socket, APR::Brigade, APR::Bucket等もuseする
PerlProcessConnectionHandler で書いたSMTPサーバの詳細 社内コード ARM/G4/Apache2/GMFilter.pm より抜粋2:handler部
PerlProcessConnectionHandler で書いたSMTPサーバの詳細 • Socket I/Oの扱い方はさまざま • ちなみにPreforkなのでchdir()してもOK
PerlProcessConnectionHandler で書いたSMTPサーバの詳細 • こうして、Post x pipeを使わずに、Perl を使ってDB接続あり、絵文字変換あり のメーリングリストサービスを実装す ることができた • サービス名: グループメール • 機会があればよろしくお願いします fi URL: http://rmail.jp/feature/4_5.html
IV. 怠惰と応用 qpsmtpd & nginx...
ゼロから書くのは面倒 • Apache2 ConnectionフェーズでSMTPサー バを実装できるのは分かった • だけどゼロから実装するなんて面倒 • ごもっとも • そんな人のために、qpsmtpdという Perl実装のSMTPサーバがあります
qpsmtpd • http://smtpd.develooper.com/ • 開発当時も存在を聞き及んでいたが、 サーバ堅牢性をうまく評価できなかっ たので採用を見送った
qpsmtpdは実は堅い • Perlのdaemonにいい印象がなかった (当時のPOE等) • qpsmtpdのEngine的な物は好みで選べる • しかもメニュー豊富
qpsmtpdは実は堅い • qpsmtpdの根底を支えるEngine=Transport • pipe (CGI like) • fork-server • prefork-server • Apache (Apache::Qpsmtpd) • async (Danga::Socket base?)
qpsmtpdは実は堅い • Apache::Qpsmtpdとか、あるし! • 後でソースコード見たら、弊社で実践 したグループメールの実装論そのもの
qpsmtpdは実は堅い • Danga::Socketベースも堅そうだし、次は qpsmtpdでも問題ないと思った • 現在もPost x pipeで稼働している、弊社 着信通知プログラムをqpsmtpdで書きか fi えたいと思うほど、今は良い印象を 持っている
qpsmtpdまとめ • なので今回「Apacheで動作するSMTP サーバ」に興味を持った方は qpsmtpd(Apache::Qpsmtpd)に触れるとい いです • プラグインもモダンな書き方で良い
nginx • http://nginx.org/ • 今話題のウェブサーバ • 今年の春にstableが登場して普及に弾み • FastCGIの動作環境としても使われる
nginxの EmbeddedPerlModule ※ http://wiki.nginx.org/EmbeddedPerlModule より
nginxの EmbeddedPerlModule ※ http://wiki.nginx.org/EmbeddedPerlModule より
nginxの EmbeddedPerlModule • すごいmod_perl1っぽい • 実際、大部分のメソッドはmod_perl1と そっくり同じ • 少し違いがあるので注意が必要な程度
nginx EmbeddedPerlModule 注意点 • worker(single threaded process)の特性上 とあるリクエストで長時間I/Oをブロッ キングするとサーバ全体が止まります • (mod_perlに比べて)ノウハウが少ない
nginx EmbeddedPerlModule まとめ • 注意点もあるが魅力的、面白そう • mod_perl1勉強しておいて良かった! • C10K問題など過酷な問題に立ち向かう 場合、性能に頭打ち感がある Apache(mod_perl)の有力な代替となりう る
まとめ
まとめ • 今でもmod_perlの知識を知っておくと、いざとい うとき良いことがある(かも) • mod_perlの文法は他の製品にも影響を与えている • Apache2/mod_perl2はConnectionも書けて、HTTP 以外の任意の(Apacheと同程度の)堅牢なサーバが Perl(mod_perl)で実装可能 • 弊社では様々な形態でmod_perlを活用しています
今回触れられなかったけど 興味深いトピック • Filter • mod_perl1 擬似Filter関連 Apache::Filter, Apache::OutputChain • mod_perl2 ネイティブFilter関連 Perl{Input,Output}FilterHandler
参考文献 • 洋書になりますが、mod_perlを勉強する上で 役立つ3冊 • Practical mod_perl • The mod_perl Developer’s Cookbook • mod_perl2 User’s Guide (Orelly 2003; http://modperlbook.org/) (Sams Publishing 2002; http://www.modperlcookbook.org/) (Onyx Neon 2007; http://modperl2book.org/)
参考文献 • Apache C API(mod_*.c)の参考書籍 • Apache拡張ガイド(上・下) (Oreilly 2000; C APIをmod_perlに沿って解説した良書でしたが 現在絶版だそうです) • The Apache Modules Book (Prentice Hall 2007; こちらは完全にC APIの本)
参考文献 • WEB+DB PRESS Vol.33 (2006/6)の連載 「Recent Perl World」で伊藤直也さんが 「【第2回】mod_perl 2.0を使い倒す」 という記事を書いています • 実際にNet::DNSを使ってDNSサーバを 書いているデモも参考になります http://gihyo.jp/magazine/wdpress/archive/2006/vol33
ご清聴 ありがとうございました