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

Unicornの設計(DESIGNより)

哲学を学び、設計も学ぶ。取り急ぎ、英文を和訳する程度。

  • Unicornは伝統的なUnixのpreforkウェブサーバ
    • スレッドを使わないことで、アプリケーションのデバッグと修正を簡単にできる
    • アプリケーションの調子が悪いとき、KILLシグナルで該当ワーカーだけを片付けることができる
  • RagelとCによるHTTPパーサはMongrelから持ってきている
  • HTTPの処理
    • すべてのHTTPリクエストヘッダを処理する
    • Rackアプリケーションを実行する
    • HTTPレスポンスをクライアントに返す
  • keepaliveやパイプライニングはサポートしない
  • 設定はRubyのeval()で行われる
    • RubyYAMLより曖昧ではなく、フック処理をインライン定義できる
  • 一つのマスタプロセスがワーカープロセス群を生み出したり片付けたりする
    • Rackアプリケーションはワーカープロセスからのみ実行される(ただし、マスタでロードすることはできる)
    • REEの様にcopy-on-writeフレンドリなガベージコレクタであれば、"preload_app true"とすることでメモリ消費を抑えられる
  • ワーカープロセスの数はCPUの数やメモリかスピンドルに応じてスケールされるべき
  • ワーカープロセスのロードバランシングはOSカーネルが行う
    • すべてのワーカープロセスは同じリスナソケット群を共有し、それらをノンブロッキングにaccept()する
    • カーネルがどのワーカープロセスにソケットを与えるか決定し、accept()しなかったワーカーはスリープする
  • ノンブロッキングaccept()により、アプリケーションがビジーでない時にクライアントのコネクションが重なるとthundering herdとなりうる
    • ワーカープロセスはアプリケーションを起動する以外はselect()/accept()するだけとし、thundering herd問題がアプリケーションに影響を及ぼさないようにする
  • 既存のprefork型サーバーを使った構成と比べれば比較的小さなthundering herds
    • プロセス数はサーバリソースに準じてスケールさせるべきで、典型的なブロッキングするprefork型サーバの様な想定クライアント数とは違う
    • 一般的なprefork型サーバにおいては数百のワーカープロセスだが、Unicornでは一般にコアあたり2から4個のプロセスとなる
  • ワーカープロセスのオンデマンドなスケーリングは自動的には行われない
    • TTINとTTOUのシグナルハンドラによってリロードなしにプロセス数を増減させることができる
  • クライアントのためにブロッキングI/Oを使う(?)
    • これによりコードパスがシンプルになる(?)
    • UnicornをLANかローカルホストのクライアント向けだけに使う場合、スレッドを使うアプリケーションは動き続ける(?)
  • タイムアウトしたワーカーを終了させるためにSIGKILLを使う
  • 各ワーカーにおいて少数のファイルディスクリプタしか使わないことで、巨大なファイルディスクリプタ集合をselect()した場合のパフォーマンス低下をさける
  • 何かしらの理由で予期せずマスタプロセスが終了してしまった場合、ワーカー群はtimeout/2秒後に終了する
  • マスタプロセスとワーカープロセスとの間にリアルタイムな依存や通信はない
    • 同調はOSカーネルによって行われ、ワーカーがクライアントを相手にしている際に共有リソースにアクセスすることはない