先週末に予定されていたJTPA企画の梅田さん主催オンラインサロンですが、会場に多くの人が集まるにつれてLingrが重くなってしまうという事態に陥ってしまい、まるでイベントの体をなさないまま時間が過ぎてしまい、あえなく中止となってしまいました。
当イベントを楽しみにしていた皆様、そして梅田さんはじめJTPAスタッフの方々には、本当に申し訳なかったと思います。ここに改めてお詫び申し上げます。
Macworld 2007のときには180人を収容して何の問題もなく快適に使えていたので、「1000人はわからないけど、200人ぐらいなら大丈夫だろう」とたかをくくっていたのが間違いでした。
今回はその反省も含めて、内部で検証した技術情報をすべて公開し、どのような問題に直面し、どのように解決にあたっているのかをお伝えすることで、特に技術者の皆さんに役立つフィードバックにしたいと思います。
■今回のアーキテクチャの変更について
まず前提としてご理解いただきたいのは、Lingrはインスタントメッセンジャーのような1対1の通信ではなく、1つのイベントが発生するごとにN人に一斉配信しなければいけない=N人全員の状態を同期させなければならないIRC型のアーキテクチャなので、ひとつのルームに参加する人数が増えると単純増加ではなくて級数的に問題が大きくなるということです。
すごく単純化して言えば、あるルームへの参加者N人中の1人あたりの平均的な信頼性を R (0<R<1) とすると、ルームの信頼性 = RN という感じで急速に信頼性が減衰していくモデルです。
これはComet技術を採用することによる「大量の開けっ放しコネクション問題」を乗り越えた他の企業、たとえば Google (GTalk in Gmail) や Meebo でさえも解いたことがない、まったく新しい種類の技術的な問題だといえます。
上記をふまえて、今回の現象を理解する前提として、1月25日のリリースで行われたバックエンドのアーキテクチャ変更についてご説明したいと思います。
新しいアーキテクチャの図はこちら。
主な変更点としては、
これらの変更点は、すべてAPIでアプリケーションを開発しやすくするために行った変更でした。pingがなくなることでクライアント側ではタイマー処理がまるまるひとつ不要になり、getがなくなることで、クライアント側でリダイレクトをハンドリングする必要なくなり、とてもシンプルなループを書けばすむようになりました。
ところが、今回発生した問題は以下のようなものでした。
もうこうなってしまうと何が起きても不思議ではありません。
アーキテクチャの変更前は、getはブラウザからWeb Serverへ直に要求されていたので、少なくともChat Serverがgetによってブロックされ、それがデッドロックを引き起こすという問題は起きたことがありませんでした。
他にもいくつか小さな問題点はあるのですが、一番クリティカルなのは上記の問題でした。とにかくgetストーム状態に落ち込んだらどうにも打つ手がありません。
これに対応するために、現在ダニーが取り組んでいるのは以下の方法です。
上記もまだ、仮説と実装と検証を繰り返す過程にあるので、最終的にどうなるかはわかりません。
他にも、
などなど、付随する小さな問題はいくつも考えられるのですが、どれもgetストーム問題によるデッドロックがroot causeであり、これさえ解決されれば自然と解消する問題なので、こいつから順番にやっつけているところです。
あとは Hot Rooms / Hot Tags の計算でボトルネックになっていたrecent messagesのカウントをmemcachedにキャッシュしたり、可能なところではどんどんフラグメント・キャッシュを使ったり、Railsを1.2.1に上げることでルーティングまわりが若干速くなったりと、パフォーマンスの改善については総合的な見直しを進めています。
近いうち、予行演習をかねて何かIT関連のネタでチャットイベントをやるかも知れません。そのときにはまた改めて告知させていただきますので、よろしくおねがいします!
♪ 中島美嘉 / 見えない星 (日テレ系水曜ドラマ「ハケンの品格」主題歌:大学時代からの親友・長瀬弘樹が作詞作曲を担当しました。おめでとう!)
※このエントリは CNET Japan ブロガーにより投稿されたものです。朝日インタラクティブ および CNET Japan 編集部の見解・意向を示すものではありません。
ごろくま on 2007/02/08
hyoshiokさん、ありがとうございます。いただいたエントリを読んで、オラクルでパフォーマンスチューニングとベンチマークに明け暮れていた頃を懐かしく思い出しました。。。今回の件は、アプリケーションレベルで取っていたログでプロファイリングを行って、問題点については正しく把握できたと考えています。これから着実に手を打っていきたいと思います。
kenn on 2007/02/07
hyoshiok on 2007/02/06
Chat ServerはなるべくDumb Serverになるようにしています。ほぼすべてのロジックとデータ生成はRails側で処理し、Chat Serverは単なるコネクションパーキング兼土管。これは、同一のnotify電文をブロードキャストするだけでクライアントがChat Server Clusterのどのマシンにぶらさがっていようが届くようにするためで、現時点のアーキテクチャにおいてスケーラビリティを確保するための肝になっています。仮にJettyをちょっと賢くして全Roomオブジェクトを持たせたとしても、Eventテーブルを引っ張るには結局Rails側に行かないと取り出せないので、本質的な解決になりえないのです。残念ながら。
kenn on 2007/02/06
読んでいて疑問に思った箇所があるので脊髄反射的なコメントです。
「3.あまりに多くのクライアントがgetを要求するため、Chat Serverの全ワーカースレッドがgetをWeb Serverに対して発行した状態でブロックされる。」
ここの部分について、Chat Server上にはRoomオブジェクトみたいなものは存在しないのでしょうか?もし、Roomオブジェクトが存在して、Web ServerからのイベントをRoomオブジェクト経由でRoom参加者に配信する形でしたら、Room上でイベントをキャッシュしてObserver接続時にRoom自身がキャッシュからイベントを配信することで Web Serverへのgetを減らせそうな気がするのですが……。
nak2k on 2007/02/06
ブログにコメントするにはCNET_IDにログインしてください。
この記事に対するTrackBackのURL:
メンバー限定サービスをご利用いただく場合、このページの上部からログイン、またはCNET_ID登録(無料)をしてください。
過去幾つかの event-id 毎に、送るべきデータ(最新状態との差分?)をサーバで生成しておいて返してやるようにすれば負荷が軽くなるのではないでしょうか。
(クライアント毎ではなくイベント発生毎に生成するのでクライアントが増えても負荷は変わらない)
これである程度の取りこぼしを許容できるので「リアルタイムにイベントを受け取れるクライアントがいなくなり、全員がgetモードへと移行する」というのが救えるはず。これと似たような仕組みを普通の周期読み込みで最近実装しました。Cometだったらわざとイベント通知を間引いて負荷調整ができたりするかも知れませんね。参考になれば幸いです。