社内勉強会でUnicornについて発表したけどあまりの準備不足に全俺が泣いたので少しずつまとめていく〜その2

Unicornの哲学(PHILOSOPHYより)

Unicornの詳細に降りていく前に、どのような哲学でもって設計・実装されているのか把握しておきたい。

Unixの哲学

「一つのことを行い、それをうまくやる。」

複雑さの排除

低速なクライアントに対応する代わりに、Unicornはバッファリング可能なリバースプロクシを信頼して代替させる。
UnicornブロッキングI/Oと旧来のpreforkモデルを採用している。この処理モデルはモダンな方法(スレッドモデル、イベントとノンブロッキングI/O)へのアンチテーゼ。

Unicornでは、ワーカープロセスの競合を考えずに済む様に設計している。

低速なクライアントについて

「低速なクライアント」と呼ぶのは、データセンタ(ローカルネットワーク)の外側にいるクライアント(物理法則的な問題)。

HTTP/1.1の永続コネクションによって遅延を減らすことができるが、クライアントがアイドルしている間はサーバリソースが無駄に消費される。
永続コネクションはワーカープロセスの一つがコネクション維持だけで何もせずに時間を消費することを意味する。シングルスレッドかつブロッキングI/Oであるため、コネクションが生きている間ワーカーはほかのクライアントに応えることはできない。
そのため、Unicornでは永続コネクションを実装しない。

ソケットバッファ以上のレスポンスや巨大なリクエストを扱う場合、クライアントコネクションの速度がワーカープロセスのボトルネックとなる。そのため、ローカルネットワークの外にいるクライアントをUnicornで受けるべきではない。

Application Concurrency != Network Concurrency

CPUとメモリは高速であるため、通常はこららによるストレスを感じることはない。Unicornでは遅いI/Oから守るためにリバースプロクシを頼る。

リバースプロクシによる性能改善

リバースプロクシを低速なI/OとUnicornの緩衝材として働かせることで余計なデータコピーによるオーバーヘッドが発生してしまうが、ローカルネットワークによるI/Oは十分に高速でHTTPリクエスト・レスポンスと比べれば無視できる。

Unicornの弱点を補うことができる理想的なリバースプロクシの条件は以下の通り。

  • すべてのHTTPリクエスト・レスポンスにとって十分なバッファを持っているべき
    • 各リクエストはリバースプロクシで受け止め、バックエンドのワーカーに可能な限り早く送るべき
    • この特徴はUnicorn用のリバースプロクシを選ぶ上で最も重要
  • ユーザスペースで過ごす時間は極小であるべき
    • ネットワークとディスクのI/Oはシステムレベルタスクで、通常はカーネルによって管理される
    • 将来的にユーザスペースのTCPスタックが一般的になればこれは変わるかもしれないが、リバースプロクシはアプリケーションのロジックによって時間を無駄にすべきではない
  • コンテキストスイッチとCPUスケジューリングによるオーバーヘッドを避けるべき
    • 大抵、ネットワークデバイスとその割り込みは一つのCPUで取り扱う
    • すべてのネットワークI/Oをシリアライズして最小のユーザスペースプロセスにまとめて競合を避けるべき
    • ネットワークI/OはCPU依存のタスクではなく、マルチコアCPUの助けを得られない。
  • 低速なクライアントのために永続コネクションとパイプライニングを効率敵に管理できるべき
  • 静的なファイルを提供できるべき
    • sendfileを使うとユーザスペースでのデータコピーを完全に避けられる

これらを満たすフリーソフトウェアはNginxしか知らない、とのこと。