改この広告は、90日以上更新していないブログに表示しています。2010-07-07UnicornでSinatraアプリをデプロイしてみた最近は仕事でSinatraアプリを書いたりしているので、Sinatraアプリを動かすためにはどのHTTPサーバを使うのがベストなのかが気になっている。(先に結論を書いておくけれど、どれがベスト、という唯一の選択肢は今のところありません。)SinatraはRackの上に構築されているので、Rackに対応したHTTPサーバーを使って動かす事になるのだが、この数がやたらと多く、どれを使えばいいのか迷う。(普通の)CGI、これぐらいは選択肢がある(いくつかHTTPサーバじゃない物も混ざっているが、Rackが対応してるという点は共通している)。WebRickはそもそもパフォーマンスに重点を置いていないし、Mongrelはメンテされてないから新規で使いたくはない、Passengerは高負荷時に関していい事を聞かない、FastCGIは情報がなくて不安、CGIは論外、という事で、これまではパフォーマンスが必要な場合はThin、そうでない場合はデフォルトで入っていて使いやすいWebRickという使い分けをしてきたのだが、最近はUnicornが気になっている。前置きがずいぶんと長くなったが、そういうわけで、Unicornを一度、試してみることにした。Unicornの特徴Unicornを紹介しているブログ記事をいくつか読んでみたのだが、UnicornはこれまでのHTTPサーバとはけっこう違うアーキテクチャを採用している事が分かった。Unicornには、マルチスレッドだのepollだの、1プロセスあたりの同時接続数を上げるための工夫は一切ない。1プロセスでは同時に処理できるリクエストはわずかに1つである。その代わりに、いくつかの工夫が施されている。RailsやSinatraのアプリをロードした後にプロセスをフォークする事ができる。これによって、コードのロードが1回しか行われずにすむので、起動が非常に高速になる。最近のサーバーだと数十プロセス程度は余裕を持って動かすことができるが、この場合、Railsを1プロセス起動するのに0.5秒ですむとしても、数十プロセスの再起動には十秒以上はかかる計算になる。起動がプロセスのフォークで済むなら再起動は1秒以内に完了する。実際には起動も並列で走らせられるだろうからここまでの差はつかないだろうけれど、フォークで済む方が圧倒的に速いのは確かだ。通常のリバースプロキシではリクエストはラウンドロビンなどで空いてそうなプロセスに送りつけられるが、Unicornでは空いているプロセスが自分でリクエストを取りにいくことができる。Socketにリクエストを書き込む。書き込まれたリクエストは空いているUnicornが取りにいく。同時に複数のUnicornプロセスが空いていた場合、OSのスケジューラーが先に動かした方のプロセスがリクエストを取って処理することになる。Unicornのアーキテクチャは、大量のアクセスを安定してさばくためのアーキテクチャである。安定して、の部分が非常に重要で、同時接続数が1つ(サーバは複数のリクエストを同時に処理しない)という事は、一見しょぼいように思われるが、並列にリクエストを処理する場合と比べてコードがシンプルに書け、シンプルであるということはバグが出にくく安定しているという事である。してもプロセスを殺さないといけない場合にも、巻き添えを食うリクエストが最小で済む。Unicornは同一プロセスでウェブアプリケーションが動く訳だが、ウェブアプリケーションにかけられる開発コストは一般にミドルウェアの開発コストよりも低い場合が多く、したがってミドルウェアほど信用は置けない。そのようなプロセスを無闇にkillする事は危険である。(本当は安全なアプリケーションを書くべきなんだけど。)Unicornがリクエストを取りにいくという点も、たまたま混雑したプロセスにリクエストを送りつける危険性がなくせるというメリットがある。リバースプロキシがしっかりしていれば、アクセスが集中したとしても、Unicornで捌ききれるだけのリクエストはハングせずに処理することができる。安定してリクエストの取りこぼしが少なく、また運用も楽である、というところに主眼が置かれたアーキテクチャであると感じた。Unicornの制限Unicornのアーキテクチャは万能ではない。Unicornを単独で使うことは避けるべきである。同時接続数が各プロセスに1つずつしかないので、インターネットのどこかのクライアントからのアクセスを直接受け付けると、例えばブラジルからリクエストが来て、3秒ぐらいプロセスを占有するかもしれない。物理的に遠くにサーバーがあるとデータの送受信にどうしても時間がかかるので、その間プロセスが占有されてしまう。いう訳で、インターネットからのリクエストを直接処理することは、Unicornにとっては致命的な性能の劣化を招く可能性がある。これは同一LAN内から負荷テストをしているだけでは見つけにくい問題であり、はまるとやっかいなので、Unicornを使う際にはこの事は頭の片隅に留めておいた方が良い。この制限を回避するために、nginxやapacheなどをリバースプロキシとしてUnicornの前に設置して、リクエストをクライアントに送信する部分は並列性の高いサーバに任せる事が推奨されている。同時接続可能数が少ないので、cometのようにコネクションを張りっぱなしにするような用途にもまったく向かない。Unicornはあくまでも短時間で実行が終わるリクエストを大量に処理するためのサーバである。Rainbows同時接続数が多い、cometなどにも使えるサーバとして、Unicornをベースに複数リクエストの同時処理機能を加えたRainbowsがある。NeverBlockなどいろいろなものをサポートしており、そのどれかを選択する事ができる。同時接続数の増加の代償として、Unicornのシンプルさはやや失われる。Unicornの方がトラブルが起こった場合にデバッグしやすい(し、トラブルも起こりにくいはずである)ので、同時接続数を増やす必要がなければUnicornを使った方が良い。実際に試してみたunicorn.confという名前で設定ファイルを書いた。これだけだとよくわからないが、Rubyのスクリプトになっている。Socketを使うことにしたので、手前にnginxでリバースプロキシを立てた。nginxの設定はこんな感じ。unicorn.confを実行したら動いた。ハマるところはなかった。動いたので、簡単にSinatra用のブログアプリでパフォーマンスの実験をしてみた。環境はlinodeの一番安いやつを使った。[reqs/sec]だった。reqs/secも出るならパフォーマンス的には充分かな。はてなブログをはじめよう!tkngさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。