apache 2.0.xから、2.2系の2.2.2へ移行して、約3ヶ月・・・
FreeBSDでportsでインストールしたapacheの話でして~~~今はapache-2.2.3になっていますが~~~なんとなく気になっていたものの、「動いてるから、どーでもいーや」と見なかったことにしていたものがあります。その気になっていたものというのは、psで見たとき
72918 ?? Ss 0:00.57 /usr/local/sbin/httpd -DNOHTTPACCEPT
72919 ?? S 0:00.01 /usr/local/sbin/httpd -DNOHTTPACCEPT
72920 ?? S 0:00.01 /usr/local/sbin/httpd -DNOHTTPACCEPT
72921 ?? S 0:00.01 /usr/local/sbin/httpd -DNOHTTPACCEPT
72922 ?? S 0:00.01 /usr/local/sbin/httpd -DNOHTTPACCEPT
72923 ?? S 0:00.02 /usr/local/sbin/httpd -DNOHTTPACCEPT
というように、「-DNOHTTPACCEPT」っていう、なんかのオプションがついていることです。NO HTTP ACCEPT・・・HTTPリクエストを受け付けないよ、って意味ですか? え、えっ~?! でも動いてるし・・・何ですか、これ?
これを、今日になって、ようやく調べてみました。
この「-DNOHTTPACCEPT」をつけているのは、/usr/local/etc/rc.d/apache22.shです。
そして、これがどういう効能を示すかというと、
/usr/local/etc/apache22/Includes/no-accf.conf
の中の、
<IfDefine NOHTTPACCEPT>
AcceptFilter http none
AcceptFilter https none
</IfDefine>
の部分に影響を与えているようです。これ、たぶん、C言語の#ifdef ~ #endifみたいなもんですよね。「-DNOHTTPACCEPT」がついているときだけ、httpとhttpsのAcceptFilterが none、つまり 無し、になるって意味っぽいです。
というわけで、次は、AcceptFilterを調べてみましょう。これですね。
http://httpd.apache.org/docs/2.2/ja/mod/core.html
Apache > HTTP サーバ > ドキュメンテーション > バージョン 2.2 > モジュール
Apache コア機能
AcceptFilter ディレクティブ
説明: プロトコルを Listen しているソケットの最適化を設定する
構文: AcceptFilter protocol accept_filter
コンテキスト: サーバ設定ファイル
ステータス: Core
モジュール: core
互換性: 2.1.5 以降Listen しているソケットに対して、OS が固有に持っているプロトコルについての最適化を 有効にするディレクティブです。大前提となる条件は、データが受信されるか HTTP リクエスト全体がバッファされるかするまで、カーネルがサーバプロセスに ソケットを送らないようになっている、ということです。現在サポートされているのは、 FreeBSD の Accept Filter と Linux のプリミティブな TCP_DEFER_ACCEPT のみです。
FreeBSD のデフォルト値は :
AcceptFilter http httpready
AcceptFilter https datareadyhttpready Accept Filter は HTTP リクエスト全体を、 カーネルレベルでバッファリングします。リクエスト全体を受信し終わると、 その後サーバプロセスにそれを送ります。詳細については accf_http(9) を参照してください。HTTPS のリクエストは暗号化されているので accf_data(9) フィルタのみが使用されます。
う~ん、なんだか難解です。英文のほうがわかりやすいかと思って見てみましたが、同じような感じ。
というわけで、ここからリンクされていた、FreeBSDの「accept_filter」という機能のマニュアルを見てみました。
http://www.freebsd.org/cgi/man.cgi?query=accept_filter&sektion=9
ほほ~、だいたいわかりました。
acceptっていってるのは、TCP/IPプログラミングでおなじみのaccept(2)のことなんですね。
どうやら、この機能を使うと、httpクライアントのリクエスト(GET /index.html ・・・みたいなデータ)を一通り受信した時点で、カーネルから、httpdプロセスへ制御が移る(たぶん、accpetシステムコールがそれまでブロックしていたのが、ようやくリターンする、など)、という動作をするようになるみたいです。
クライアントがソケットにつないだ時点では、apacheのhttpdプロセスは、とくに実行すべき仕事はまだありません。だから、そのときhttpdプロセスに実行権がやってきても、とくに何もせずにそのままカーネルへ戻すのでしょう。具体的なリクエストが届いた時点で、ようやく、httpdの仕事が生まれるということになるはずです。この、カーネルとhttpdとの間でコンテキストスイッチが無駄に発生しているのを防ぐことができる、というのが、この機能のミソということのようです。
たぶん、これで効果が出るのは、比較的大規模のウェブサイトの場合、きっと、大量のリクエストがやってくる、とっても忙しいウェブサーバなんじゃないかな、という気がします。暇なサーバだと、あんまり効果なさげなかんじ。
この処理をやっているカーネルのソースコードは、
/usr/src/sys/netinet/accf_http.c
のようです。
こちらからソースコードが参照できます。
どうやら、基本的には、バッファ内のデータに対して、文字列比較をやっているだけっぽいかんじ、まさに、HTTPのリクエスト文字列が貯まったかどうかの判定をやっているかんじです。
この機能は、カーネルモジュールのaccf_http.koをロードすれば、使えるようになるみたいです。
☆ ☆ ☆ ☆ ☆ ☆
さてさて、「-DNOHTTPACCEPT」がついているということは、この機能が使われない、ということです。なんか、もったいない! せっかくのサーバ最適化機能ですから、使わなきゃ!!
というわけで、もう一度、/usr/local/etc/rc.d/apache22.shを読んでみます。
それでわかったのは、基本的には、/etc/rc.confに、
apache22_http_accept_enable="YES"
を追加すればよいようです。
apache22.shをじっくりとおっかけてみると、accf_http.koというカーネルモジュールのロードもやってくれているので、loader.confでわざわざロードするように書かなくても大丈夫です(googleで見つけた情報によれば、jail内でhttpdを動かすときは、jail内でkldloadできないため、あらかじめロードしておく必要があるそうです)。
☆ ☆ ☆ ☆ ☆ ☆
これで、試してみました。
psで見ると
72990 ?? Ss 0:00.59 /usr/local/sbin/httpd
72991 ?? I 0:00.02 /usr/local/sbin/httpd
72992 ?? I 0:00.02 /usr/local/sbin/httpd
72993 ?? I 0:00.17 /usr/local/sbin/httpd
72994 ?? I 0:00.42 /usr/local/sbin/httpd
72995 ?? I 0:00.02 /usr/local/sbin/httpd
73019 ?? I 0:00.02 /usr/local/sbin/httpd
73020 ?? I 0:00.02 /usr/local/sbin/httpd
「-DNOHTTPACCEPT」がつかなくなっています。
で、効果のほどは・・・う~ん、よくわかりません(笑)。
そんなに忙しいウェブサーバじゃないからでしょうね。
1秒間に、数百程度のリクエストがあるくらいになると、だいぶ違ってくる気がしますけど。
なんと詳しい説明。すばらしいです。
返信削除助かりました。ありがとうございます。
返信削除