Hacking Apache HTTP Server at Yahoo!

遅ればせながら Hacking Apache HTTP Server at Yahoo! を一通り読みました。これは Yahoo! のエンジニアである Michael J. Radwin 氏が昨年末の ApacheCon で喋ったときのスライド。

Yahoo! が Apache に独自に手を入れた "yapache" なる Apache を使ってるというのはときどき聞いてたんですが、具体的にどういう変更が入ってるかとかは謎のままでした。このスライドにはその辺りの話が惜しげもなく書いてあって、とても面白い。

シグナルを使わないでログをローテーションするとか、ログフォーマットを工夫してるとか、出力の HTML にホスト名などを記載しておいてサポートに役立てるとか、いろいろ参考になる点は多かったのですが、一番興味が引かれたのはやはり "Waiting for the Client Sucks" の辺り。OS を Apache の手前に挟まれたバッファリングの層とみなして活用して、Apache にはダイナミックコンテンツを出力するだけに専念させろ、バッファリングは OS にやらせろ、みたいな話が書いてます。

FreeBSD の httpready なるフィルタを使って、HTTP リクエストをカーネル側で一端バッファリングし、Apache はバッファからまとめてリクエストの内容を取得する。すると、リクエスト全体を最初の read() でまとめて読むことができて、Apache のプロセスがリクエストを読み取るまでの間に占有される時間がなくなり、結果としてリソースを有効活用できる、ってなことが書いてます。

httpready っていうのは、Apache に標準で組み込まれている AcceptFilter のことらしい。FreeBSD のカーネルの機能(?) の accf_http が有効になってると使える AcceptFilter で、これを設定しておくとカーネルでの HTTP リクエストバッファリングが効くそうです。なお、Apache のドキュメント曰く、Linux の TCP 層には同一のことをする実装はサポートされていないようです。

Linux の TCP_DEFER_ACCEPT は HTTP リクエストのバッファリングをサポートしていません。none 以外の値でTCP_DEFER_ACCEPT が有効になります。詳細については Linux man ページ tcp(7) を参照してください。

カーネルレベルでのバッファリングをサポートしていない、とはありますが実際に Linux がそのときどういう風に動いてるかまでは言及されてないので、その辺はよくわかりません。

また、受け付けるリクエストだけでなく、出力側のバッファリングにも工夫をしているようで、FreeBSD なら kern.ipc.maxsockbuf、Linux なら net.core.wmem_default と net.core.wmem_max の値を最も大きなレスポンスのサイズ (HTML + HTTPヘッダのサイズ)まで引き上げて、OS 側の送信用のバッファ量を増やすということをやる。この状態で、ソケットのバッファにレスポンスのデータをまとめて書き込んでやって、すぐにソケットを閉じると。(このソケットの扱いをスライドの中では NO_LINGCLOSE と呼んでる様子。)こうすることで Apache のプロセスがすぐにプールに帰るようになり、効率がいいとのことです。クライアントが全部を受信したかどうかは無視。

ソケットバッファにまとめて書き込んですぐクローズとかっていう辺りは、Apache のコードに直接手を入れて実現するのかなあ。

OS のバッファリングの機能を最大限活かす、という方針はよく分かりましたが、気になったのはリバースプロキシとかが噛んでる場合はどうなんだろうなあというところ。Yahoo! ではパフォーマンスが要求される所では Apache API を使って C でアプリケーションを記述するらしいのですが、そうするとそのアプリケーションを積んだ Apache が直接クライアントに応答することになって、OS をクライアントと Apache の間に挟まるバッファリングの層とみなすことができます。

一方、リバースプロキシ + mod_perl のような環境ではリバースプロキシがバッファの役割を果たします。リバースプロキシのパフォーマンスを目一杯あげたいという時には yapache の考え方が応用できそうですが、バックエンドに控える mod_perl が載ってる OS にはどういうチューニングを加えるのが理想的なのか。mod_proxy とバックエンドのサーバーが高速な LAN で繋がってる環境でも、HTTP リクエストのカーネルレベルでのバッファリングは効果的なのか。その辺詳しく記述してるものとかはないかな。Practical mod_perl にはカーネルパラメータのチューニング方法とかが若干載ってた気がするけど、そこまで突っ込んだことは書いてなかったような気もする。ちょっと読み返してみよう。