ページの先頭です


ページ内移動用のリンクです

高速WebサーバMighttpdのアーキテクチャ

2012年5月29日

IIJ-II技術研究所では、2009年の秋からMighttpdblank(mightyと読む)というWebサーバの開発を始め、オープンソースとして公開しています。この実装を通じて、マルチコアの性能を引き出しつつ、コードの簡潔性を保てるアーキテクチャにたどり着きました。ここでは、各アーキテクチャについて順を追って説明します。

ネイティブ・スレッド

伝統的なサーバは、スレッド・プログラミングという手法を用いています。このアーキテクチャでは、1つのコネクションを1つのプロセスかネイティブ・スレッドが処理します。

このアーキテクチャは、プロセスやネイティブ・スレッドを生成する方法で細分化できます。「プール」方式では、あらかじめ複数を起動しておきます。例としては、Apacheのpreforkというモードが挙げられます。「都度」方式では、コネクションを受け取るたびに生成します。このアーキテクチャの利点は、制御を占有できるので、見通しのよいコードが書けることです。また、カーネルがプロセスやネイティブ・スレッドを適切なコアに割り当ててくれますので、コアを偏りなく利用できます。欠点は、プロセスを切り替えるためにカーネルとプロセスのコンテキストスイッチが多発して、性能が出ないことです。

スレッド・プログラミング

※ スレッド・プログラミングでは、制御を占有できるため見通しのよいコードとなる。

イベント駆動

最近では、高速なサーバを実現するためにはイベント駆動プログラミングが必須であると言われています。このアーキテクチャでは、複数のコネクションを1つのプロセスで処理します。利用しているWebサーバとしては、lighttpdが挙げられます(Mighttpdの名は、lighttpdからいただいています)。

プロセスを切り替える必要がありませんから、コンテキストスイッチが低減され、性能が出るようになります。これが利点です。一方、欠点は2つあります。1つは、プロセスが1つしかないため、1つのコアしか活用できないこと。もう1つは、非同期プログラミングをしなければならず、コードがイベントハンドラという単位に分断されることです。非同期プログラミングでは、例外処理が今までと同じようには利用できないという問題もあります(C言語には例外はありませんが)。

イベント駆動プログラミング

※ イベント駆動プログラミングでは、コードがぶつ切りとなり見通しが悪くなる。

1コア1プロセス・マッピング

マルチコアを活用するために、イベント駆動のプロセスをコア数だけ起動すればよいというのは、多くの方が思いつくアイディアでしょう。Webサーバであれば、80番ポートを共有しなければいけませんが、preforkという手法を使えばコードを少し変更するだけで、ポートの共有を実現できます。僕はこれを1コア1プロセス・マッピングと呼んでいます。

このアーキテクチャを採用しているWebサーバとしては、nginxが挙げられます。また、node.jsはイベント駆動のアーキテクチャを使っていましたが、最近1コア1プロセス・マッピングも実装しました。

このアーキテクチャの利点は、マルチコアをすべて活用し、かつ性能を引き出せることです。しかし、プログラムの見通しが悪いという欠点は解決されていません。

1コア1プロセス・マッピング

軽量スレッド

僕は、コードの見通しが悪いという問題を解決するために、純粋関数型言語Haskellの主要コンパイラであるGHC(Glasgow Haskell Compiler)が提供している軽量スレッドを利用しました。軽量スレッドとは、イベント駆動の上に実現されているユーザ空間のスレッドです。最近のコンピュータであれば、10万個起動しても問題なく動作します。過去にはユーザ空間のスレッド提供する言語やライブラリがありましたが、軽量でない、問題が生じ易いなどの理由から、現在ではあまり利用されていません。GHCでは、Haskellの純粋関数型言語の性質がうまくマッチして、軽量スレッドを提供することに成功しています。

軽量スレッドを使えば、従来のスレッド・プログラミングと同様、見通しのよいコードが書けます。また、軽量スレッドを切り替えるのはGHCのランタイムですから、コンテキストスイッチは起こりません。

軽量スレッド

※ 軽量スレッドでは、制御を占有できるため見通しのよいコードとなる。

Mighttpd

コアごとにプロセスを起動してすべてのコアを活用し、イベント駆動で性能を引き出し、その上で軽量スレッドを使ってコードを簡潔に保つ方法は、現時点では最良のサーバ・アーキテクチャだと思いますが、1つ注意点があります。

サーバがたくさんシステムコールを発行すると、思ったほど性能が出ないのです。これは、システムコールを発行すると、コンテキストスイッチが起こって、CPU時間がカーネルに割り当てられ、すべての軽量スレッドが止まってしまうことが原因です。そのためMighttpdでは、例えばファイルに対する stat() システムコールの結果を一定時間キャッシュし、システムコールの発行数を低減しています。

関数型言語は遅いとか実用的でないと誤解している方もたくさんいらっしゃるようですが、我々のベンチマークでは、Mighttpdはnginxと比肩する性能を出しています。

山本 和彦

執筆者プロフィール

山本 和彦(やまもと かずひこ)

株式会社IIJイノベーションインスティテュート(IIJ-II)技術研究所 主幹研究員
1998年IIJ入社。開発した代表的なオープンソフトにMew、Firemacs、Mighttpdがある。「プログラミングHaskell」の翻訳者。職場ではHaskell、家庭では二人の男の子と格闘する日々を送っている。

関連リンク


ページの終わりです

ページの先頭へ戻る