mod_perl温故知新〜Perl CGIの高速化からメールサーバまで〜

2.2K Views

February 24, 24

スライド概要

2011/07/16に Hokkaido.pm#5 で発表したスライドです。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

mod_perl温故知新 〜Perl CGIの高速化から メールサーバまで〜 株式会社fonfun 尾形 鉄次 (OGATA Tetsuji) Twitter: @xtetsuji 2011/7/16

2.

自己紹介など

3.

自己紹介(1/2) • 尾形 鉄次 (OGATA Tetsuji) Twitter: @xtetsuji • http://post.tetsuji.jp/ (最近ブログ始めました) ここで語りきれないことも書きたいです • 北海道の音更町(帯広市の隣町)出身です。 高校卒業までの18年間を北海道で過ごしま した

4.

自己紹介(2/2) • 人生初スピーカー (お手柔らかに…) • (ポスト)モダンPerlに乗れていない30代 • JavaScript / Shell Script(bash) // Emacs Lisp • 趣味: クラシック音楽、カフェ散策、 路線バス

5.

所属紹介(1/2) • 株式会社fonfun(フォンファン) http://www.fonfun.co.jp/ • 主力製品:リモートメール http://rmail.jp/ • 任意団体g15アソシエーション http://g15.jp/

6.

所属紹介(2/2) • 株式会社fonfun(旧社名:ネットビレッジ) • 1999年からウェブメール (商品名:リモートメール)を運用 • 2003年に第4世代システム(Perl + Apache/ mod_perl + Oracle + Post x)を開発 fi • 最近新しい技術的な試みにチャレンジ中

7.

今回お話をさせていただく ことになった経緯 • 地元にスカイアークシステムという会社がある • YAPC::Asia2010でスポンサーをしているのを発見 • 興味があると言っていたら、とある人が中の人 である永谷さん(@onagatani)を紹介してくれた • しかも Hokkaido.pm の偉い人だった! • いまここ

8.

Agenda

9.

Agenda • I. mod_perlを振り返ってみませんか? • II. mod_perlの真髄はApacheの拡張 • III. Apache2&mod_perl2でHTTPの外へ • IV. 怠惰と応用 - qpsmtpd & nginx... • まとめ

10.

I. mod_perlを 振り返ってみませんか?

11.

いまさら 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

12.

いまさら Apache+mod_perl? • Apache+mod_perl+WAFでもmod_perlの 内側を多少知っておけば… • バグを踏んだ時、役立つ • パフォーマンス改善のヒントがある

13.

いまさら Apache+mod_perl? • 今もApacheは最も使われているサーバ • どのLinuxディストリビューションにも 大抵Apacheとmod_perlのパッケージは 標準で用意されている(はず)

14.

いまさら 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

15.

いまさら Apache+mod_perl? • いつでもゼロから最新のWAFを使って 開発ができるとは限らない • 受託案件やコンテンツ移管案件など、 レガシーなApacheとPerl CGI(or PHP)の 製品を渡されることもある大人の世界 • リファクタリング、 マイグレーション...

16.

mod_perlのバージョン • Apache1.3 → mod_perl1 • Apache2.x → mod_perl2 • 1と2ではmod_perlの文法の違いは大きい • 歴史の狭間のmod_perl1.99(要注意) • ※現在Apache1はdeprecatedだが、世間では現役と言えるので、mod_perl1 についても、なるべく触れます

17.

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 より)

18.

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 が高速化する

19.

例えば hello.pl

20.

mod_perl1でHello1.pm • 「レスポンスハンドラ」を書く • レスポンスハンドラとは、第一引数にリク エストオブジェクト($r)を受け取る handler サブルーチンが定義されたパッケージ • http://hello.example.jp/ 以下にアクセスした ら、遍く Content-Type: text/plain で定形の挨 拶するだけのサンプルを作ってみる

21.

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>

22.

mod_perl1でHello1.pm

23.

mod_perl2でHello2.pm • 同様のサンプルを作成 • 基本はmod_perl1と同じ • 文法は結構違います

24.

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>

25.

mod_perl2でHello2.pm • PerlHandler → PerlResponseHandler 名前がようやく明快になった • ただ Hello2.pm のコードは結構違います

26.

mod_perl2でHello2.pm

27.

mod_perl2でHello2.pm • mod_perl1はmod_perlのパッケージ名前 空間は Apache:: のみ • mod_perl2はmod_perlのパッケージ名前 空間が Apache2::、ModPerl::、APR:: 等と 細分化され、mod_perl1の巨大なクラス (Apache object等)もMix-in的に分割された

28.

Helloと挨拶しても... • 今までやってきたのはCGIやWAFでも できる簡単HTTPレスポンスを返す処理 • ようやく本題へ...

29.

II. mod_perlの真髄は Apacheの拡張

30.

mod_perlの真髄は Apacheの拡張 • 今まで見てきたのは、HTTPレスポンス を返す処理 • mod_perlはHTTPレスポンスを返すだけ でなく、その前処理や後処理もできる

31.

Apacheの内部処理 • MPMは多く使われているPreforkを仮定 • 親プロセスを起動すると、設定に従っ て子プロセスを設定の数だけ起動 (prefork) • 各子プロセスはHTTPリクエストを待つ • では子プロセスごとのHTTP処理は...

32.

Apacheの内部処理 • リクエストを受ける • ヘッダを解析する • 必要に応じてURLを変換したり、DocumentRootを手がかりに実パス を割り出す • アクセス制御、認証、承認(BASIC認証など) • MIMEタイプを考える • レスポンスを出す(静的ファイル、プログラム出力) • ログ(アクセスログ、エラーログ)を出力する • 次のリクエストを待つ

33.

Apacheの内部処理 • ざっくり列挙しただけなのにビッシリ • 各処理を行っている部分にはフックがあって、 ここに各種処理を挟むことが可能 →Apache Module(mod_xxxxxx) • Apache Module でできることを Perl ででき るようにしたものがmod_perl • これがmod_perlの真髄です

34.

Apache/mod_perl 処理フェーズ • 各フックがある部分: 「(処理)フェーズ」 • リクエスト待ち状態から各フェーズを一 巡するので、全体を「リクエストサイク ル」と呼びます • Apache/mod_perlの1と2で結構違います • Apache/mod_perlの2のほうが高機能

35.

処理フェーズ: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

36.

処理フェーズ:mod_perl1 ※「Practical mod_perl」より抜粋

37.

処理フェーズ: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

38.

処理フェーズ:mod_perl2 ※「Practical mod_perl」より抜粋

39.

実例 • 時間の都合上、多くをご紹介できませ んが、少し実例を見てみましょう

40.

実例: mod_rewriteの リファクタリング • よく使われているご存知mod_rewrite • 単純なURL正規表現置換に便利 • ただ、処理行数が多くなってきたり、 (頑張れば書けるが)条件分岐や反復処理 に手を出すと途端に黒魔術化

41.

実例: mod_rewriteの リファクタリング • mod_rewrite処理を、URLを書き換える フェーズのPerlTransHandlerで実装する • (使うなら)条件分岐や反復処理がPerlの コードになるので可読性がよくなる • mod_rewrite自身PerlTransHandlerに対応 するC APIのフェーズで動作しています ※実際はPerlFixupHandlerに対応するフェーズでもお仕事をしています

42.

実例: mod_rewriteの リファクタリング # required “RewriteURL.pm” <VirtualHost A.B.C.D:80> ServerName hello.example.jp PerlTransHandler RewriteURL </VirtualHost>

43.

実例: mod_rewriteの リファクタリング

44.

実例: mod_rewriteの リファクタリング • 「レスポンスハンドラ」と同じような 文字通り「ハンドラ」を書きます • sub handler { my $r = shift; ... } • 他のフェーズでもこの書き出しは定石

45.

他にできること一例 • 各フェーズでこんなことができるよ! という概念だけご紹介します。 • 実際の体験談を多く盛りこんでみます

46.

他にできること一例 • PerlPostReadRequestHandler 取り急ぎ悪質なDDoSを遮断(応急措置) • return DONE; # cut off! • 本来はPerlAccessHandlerでするのが良い • 最終的にはLBやiptablesで遮断かな

47.

他にできること一例 • PerlTransHandlerは本当に役立ちます • mod_rewriteがスイス製アーミーナイフ なら、PerlTransHandlerはプロの料理人 が使う高級包丁?それとも日本刀? • URL書き換えだけでなくリダイレクトも 当然できます(return REDIRECT; など)

48.

他にできること一例 • Perl{Authen,Authz}Handler シングルサインオン実装 PHPでもレスポンス処理に入る前に Cookie処理などをPerlで行える • Access,Authen,Authzの3種は3Aとも呼ば れる。アクセス制御、認証、許可。

49.

他にできること一例 • 既に大量のHTMLファイルのある静的サイトを受 け取ったプログラマの孤軍奮闘 • 依頼「User-Agentを見て *.html の Content-Type を text/html か application/xhtml+xml か、出し分けして いただけますか?」 • PerlTypeHandler か PerlFixupHandler でやりましょう (入出力ヘッダ処理: $r->headers_in, $r->headers_out)

50.

他にできること一例 • 依頼「アフィリエイトで全てのHTML ファイルの</body>の前に動的にコード を入れていただき(ry」 • PerlHandler / PerlResponseHandler を使え ばお手軽に解決 fi • $r->print(thiswork(slurp($r-> lename())));

51.

他にできること一例 • 依頼「iモードHTMLで書かれた絵文字 入り静的HTMLが大量にあるんですが、 SBM端末が来たら変換(ry」 • これも PerlHandler / PerlResponseHandler でお手軽かつ透過 的に解決しましょう

52.

他にできること一例 社内コード Kepler/Apache/ConvertEmoji.pm より抜粋

53.

他にできること一例 • HTTPレスポンス出力処理 / PerlHandler / PerlResponseHandler が完了した後... • PerlLogHandlerは$rからHTTPレスポンス 結果や送信サイズ数が取得可能な状態 • 様々なDBにログを書き出したり通知を したりできます

54.

他にできること一例 • 他にも本当に色々な使い方ができる mod_perlのHTTP処理ですが、そろそろ 次の話に移ります

55.

III. Apache2&mod_perl2 でHTTPの外へ

56.

Apache2&mod_perl2で HTTPの外へ • 先ほど紹介したmod_perlの処理フェー ズはHTTP Request/Responseのお話 • $r : Apache (mod_perl1) • $r : Apache2::RequestRec (mod_perl2) • 慣習名 $r の r は request/response の r

57.

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 を参照

58.

Apache2/mod_perl2 Connection サイクル • 今回はConnectionサイクルに注目 • (HTTPではない)Protocolサイクルとも • ConnectionサイクルはHTTPを扱う サイクル・フェーズよりも前に位置する • Apache2での応用例: mod_ssl

59.

Connection サイクルの 処理フェーズ PerlPreConnectionHandler PerlProcessConnectionHandler ※「Practical mod_perl」より抜粋

60.

Connection サイクルの 処理フェーズ • 2つだけ。HTTPよりもシンプル? • PerlPreConnectionHandler • Apache処理の本当に冒頭に位置する • PerlProcessConnectionHandler • 今回ここにSMTPサーバを作ります

61.

Apache2 SMTPサーバの 作成動機 • 開発内容はメーリングリストサービス • Post x pipeを使えばPerlプログラムでI/O 処理が書けるが、forkのコストが高い fi • 重いDB処理、絵文字処理がしたかった

62.

Apache2 SMTPサーバの構 成 • ハードウェア構成: 2台(load balancing) • 外部との送受信SMTPサーバは、既に 稼働中の別のハードウェア数十台程 度のクラスタがある • 「Apache2 SMTPサーバ」の内部では、 fi Post xに受信と送信の処理を依頼...

63.

Apache2 SMTPサーバの構 成 • 矢印はSMTPの流れ • サーバ内で両端でPost xを構え させているのは、煩雑なキュー の管理を省きたい思惑がある • Apache2 SMTP サーバが1つの Post xから見て別ポートで動作 fi fi しているコンテンツフィルタの ように稼働する

64.

mod_perl2で PerlProcessConnectionHandler • 「コネクションハンドラ」を書く • 今までの$rと違い$c (Apache2::Connection) オブジェクトを第一引数に受け取る • sub handler { my $c = shift; ... } • Apacheの設定 • 実際の例を紹介しますが、長いので抜粋

65.

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>

66.

PerlProcessConnectionHandler で書いたSMTPサーバの詳細 社内コード ARM/G4/Apache2/GMFilter.pm より抜粋1:冒頭部分

67.

PerlProcessConnectionHandler で書いたSMTPサーバの詳細 • ポイント:重たいモジュールを大量に useしても大丈夫! • $cのために use Apache2::Connection • Socket I/O処理のためにAPR::Socket, APR::Brigade, APR::Bucket等もuseする

68.

PerlProcessConnectionHandler で書いたSMTPサーバの詳細 社内コード ARM/G4/Apache2/GMFilter.pm より抜粋2:handler部

69.

PerlProcessConnectionHandler で書いたSMTPサーバの詳細 • Socket I/Oの扱い方はさまざま • ちなみにPreforkなのでchdir()してもOK

70.

PerlProcessConnectionHandler で書いたSMTPサーバの詳細 • こうして、Post x pipeを使わずに、Perl を使ってDB接続あり、絵文字変換あり のメーリングリストサービスを実装す ることができた • サービス名: グループメール • 機会があればよろしくお願いします fi URL: http://rmail.jp/feature/4_5.html

71.

IV. 怠惰と応用 qpsmtpd & nginx...

72.

ゼロから書くのは面倒 • Apache2 ConnectionフェーズでSMTPサー バを実装できるのは分かった • だけどゼロから実装するなんて面倒 • ごもっとも • そんな人のために、qpsmtpdという Perl実装のSMTPサーバがあります

73.

qpsmtpd • http://smtpd.develooper.com/ • 開発当時も存在を聞き及んでいたが、 サーバ堅牢性をうまく評価できなかっ たので採用を見送った

74.

qpsmtpdは実は堅い • Perlのdaemonにいい印象がなかった (当時のPOE等) • qpsmtpdのEngine的な物は好みで選べる • しかもメニュー豊富

75.

qpsmtpdは実は堅い • qpsmtpdの根底を支えるEngine=Transport • pipe (CGI like) • fork-server • prefork-server • Apache (Apache::Qpsmtpd) • async (Danga::Socket base?)

76.

qpsmtpdは実は堅い • Apache::Qpsmtpdとか、あるし! • 後でソースコード見たら、弊社で実践 したグループメールの実装論そのもの

77.

qpsmtpdは実は堅い • Danga::Socketベースも堅そうだし、次は qpsmtpdでも問題ないと思った • 現在もPost x pipeで稼働している、弊社 着信通知プログラムをqpsmtpdで書きか fi えたいと思うほど、今は良い印象を 持っている

78.

qpsmtpdまとめ • なので今回「Apacheで動作するSMTP サーバ」に興味を持った方は qpsmtpd(Apache::Qpsmtpd)に触れるとい いです • プラグインもモダンな書き方で良い

79.

nginx • http://nginx.org/ • 今話題のウェブサーバ • 今年の春にstableが登場して普及に弾み • FastCGIの動作環境としても使われる

80.

nginxの EmbeddedPerlModule ※ http://wiki.nginx.org/EmbeddedPerlModule より

81.

nginxの EmbeddedPerlModule ※ http://wiki.nginx.org/EmbeddedPerlModule より

82.

nginxの EmbeddedPerlModule • すごいmod_perl1っぽい • 実際、大部分のメソッドはmod_perl1と そっくり同じ • 少し違いがあるので注意が必要な程度

83.

nginx EmbeddedPerlModule 注意点 • worker(single threaded process)の特性上 とあるリクエストで長時間I/Oをブロッ キングするとサーバ全体が止まります • (mod_perlに比べて)ノウハウが少ない

84.

nginx EmbeddedPerlModule まとめ • 注意点もあるが魅力的、面白そう • mod_perl1勉強しておいて良かった! • C10K問題など過酷な問題に立ち向かう 場合、性能に頭打ち感がある Apache(mod_perl)の有力な代替となりう る

85.

まとめ

86.

まとめ • 今でもmod_perlの知識を知っておくと、いざとい うとき良いことがある(かも) • mod_perlの文法は他の製品にも影響を与えている • Apache2/mod_perl2はConnectionも書けて、HTTP 以外の任意の(Apacheと同程度の)堅牢なサーバが Perl(mod_perl)で実装可能 • 弊社では様々な形態でmod_perlを活用しています

87.

今回触れられなかったけど 興味深いトピック • Filter • mod_perl1 擬似Filter関連 Apache::Filter, Apache::OutputChain • mod_perl2 ネイティブFilter関連 Perl{Input,Output}FilterHandler

88.

参考文献 • 洋書になりますが、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/)

89.

参考文献 • Apache C API(mod_*.c)の参考書籍 • Apache拡張ガイド(上・下) (Oreilly 2000; C APIをmod_perlに沿って解説した良書でしたが 現在絶版だそうです) • The Apache Modules Book (Prentice Hall 2007; こちらは完全にC APIの本)

90.

参考文献 • 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

91.

ご清聴 ありがとうございました