最終更新時刻:2008年10月10日(金) 23時50分

-

梅田サロン中止のお詫び、およびアーキテクチャ変更についての技術詳細レポート

公開日時:
2007/02/06 14:09
著者:
kenn

先週末に予定されていた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日のリリースで行われたバックエンドのアーキテクチャ変更についてご説明したいと思います。

新しいアーキテクチャの図はこちら。

LingrBackend20070125

主な変更点としては、

  1. pingをクライアントから直接打たず、observeを発行した時点でChat Serverが代替わりしてくれるようにした(クライアント側での処理をサーバ側へオフロードした)
  2. Cometを使ったobserveのループにおいて、レスポンスを返してから再接続までの瞬間に発言されたメッセージを漏らさないため、observe発行の際にはクライアント側で受け取った最終のevent-idを申告させ、これとサーバ側で持っているevent-idにずれが検出された場合(クライアント側が遅れていた場合)には即座にChat Serverがobserveをクライアントへ返してgetというまとめ読みメソッドへとリダイレクトさせていたのだが、このget処理もChat Serverが代替わりしてくれるように変更。つまりクライアント側ではobserveからの戻りだけを意識すればよくなった。

これらの変更点は、すべてAPIでアプリケーションを開発しやすくするために行った変更でした。pingがなくなることでクライアント側ではタイマー処理がまるまるひとつ不要になり、getがなくなることで、クライアント側でリダイレクトをハンドリングする必要なくなり、とてもシンプルなループを書けばすむようになりました。

ところが、今回発生した問題は以下のようなものでした。

  1. 多人数が一斉にひとつの部屋に集まると、1秒間に数〜数十ものイベントが発生する。(いわゆる発言だけに限らず、部屋に入る、部屋を去る、ニックネームをセットする、などもすべてひとつのイベント)しかも、その全てのイベントが全員に毎回マルチキャストされるので、掛け算でSocket I/O負荷が集中する。
  2. 1秒間に数〜数十ものイベントが発生すると、ほぼ全てのクライアントで自分が持っているevent-idがサーバ側の最新状態より遅れてしまうので、ほぼ全てのクライアントからのobserveリクエストがWeb Serverに対してgetを要求するものになる。
  3. あまりに多くのクライアントがgetを要求するため、Chat Serverの全ワーカースレッドがgetをWeb Serverに対して発行した状態でブロックされる。
  4. get要求を受けているWeb Serverのほうでは、そもそもRailsの負荷が高くなっているところへ、enterやsayなどのイベントをリアルタイムにChat Serverにnotifyしようとしても、Chat Serverのほうでスレッドが飽和しているためにどんどんキューに詰まっていく一方となり、まったく配信されない。そしてRails側の処理もnotifyの完了を待つためブロックされてしまう。=いわばデッドロック状態ということになる。
  5. notifyによる配信が完全に途絶えた結果、誰一人としてリアルタイムにイベントを受け取れるクライアントがいなくなり、全員がgetモードへと移行する。
  6. さらに、全体のサイクルがどんどん遅くなっているので、クライアントがgetの結果をobserve経由で受け取ったときには常にevent-idが古くなっているため、ますますgetに依存するようになる。20秒毎にタイムアウトで空いたRailsをつかって細々とgetが処理されていくが、全然追いつかない。
  7. つまり結果としてWeb Serverがパンクした状態でさらにgetループ(=インターバル・ゼロのビジーポーリング)に陥ってしまうので、ますますWeb Serverの負荷が増える、という最悪の事態に。

もうこうなってしまうと何が起きても不思議ではありません。

アーキテクチャの変更前は、getはブラウザからWeb Serverへ直に要求されていたので、少なくともChat Serverがgetによってブロックされ、それがデッドロックを引き起こすという問題は起きたことがありませんでした。

他にもいくつか小さな問題点はあるのですが、一番クリティカルなのは上記の問題でした。とにかくgetストーム状態に落ち込んだらどうにも打つ手がありません。

これに対応するために、現在ダニーが取り組んでいるのは以下の方法です。

  1. getを同期処理にするのをやめ、BackgrounDRbを使用して非同期にする。つまり、Railsのほうではgetリクエストを受け付けたら即座にChat Serverにレスポンスを返し、実際の処理はRailsから分離された別プロセス(BackgrounDRb)で行い、ここから別便でChat Serverに届けるようにイベントチェーンすることで、誰かが誰かにブロックされる部位を極小化する。
  2. いつもChat Serverはスカスカ、Web Serverが忙しいという傾向にあるので、サーバの構成比を見直し、Web Serverの比率を高くする。もちろんJettyのスレッドプール数もチューニングする。また、サーバ自体も追加する。

上記もまだ、仮説と実装と検証を繰り返す過程にあるので、最終的にどうなるかはわかりません。

他にも、

  1. 全てのイベントデータにはoccupantsという、anonymous observerも含めた全員分の参加者リストが毎回含まれているので、参加者が200人もいると、それだけで毎回発行されるイベントデータが巨大になった。
  2. レスポンスが遅くなればなるほどユーザがブラウザの再読込ボタンをクリックしてしまう確率が上がった。(再読込はダントツで負荷が高いが、こういうときのユーザのふるまいとしては当然なので仕方がない)
  3. 多数のLingr Radarが自動でリトライを続けたのでさらに負荷が上がった。

などなど、付随する小さな問題はいくつも考えられるのですが、どれもgetストーム問題によるデッドロックがroot causeであり、これさえ解決されれば自然と解消する問題なので、こいつから順番にやっつけているところです。

あとは Hot Rooms / Hot Tags の計算でボトルネックになっていたrecent messagesのカウントをmemcachedにキャッシュしたり、可能なところではどんどんフラグメント・キャッシュを使ったり、Railsを1.2.1に上げることでルーティングまわりが若干速くなったりと、パフォーマンスの改善については総合的な見直しを進めています。

近いうち、予行演習をかねて何かIT関連のネタでチャットイベントをやるかも知れません。そのときにはまた改めて告知させていただきますので、よろしくおねがいします!

中島美嘉 / 見えない星 (日テレ系水曜ドラマ「ハケンの品格」主題歌:大学時代からの親友・長瀬弘樹が作詞作曲を担当しました。おめでとう!)

※このエントリは CNET Japan ブロガーにより投稿されたものです。シーネットネットワークスジャパン および CNET Japan 編集部の見解・意向を示すものではありません。

このエントリーへのコメント

5

過去幾つかの event-id 毎に、送るべきデータ(最新状態との差分?)をサーバで生成しておいて返してやるようにすれば負荷が軽くなるのではないでしょうか。
(クライアント毎ではなくイベント発生毎に生成するのでクライアントが増えても負荷は変わらない)
これである程度の取りこぼしを許容できるので「リアルタイムにイベントを受け取れるクライアントがいなくなり、全員がgetモードへと移行する」というのが救えるはず。これと似たような仕組みを普通の周期読み込みで最近実装しました。Cometだったらわざとイベント通知を間引いて負荷調整ができたりするかも知れませんね。参考になれば幸いです。

  ごろくま on 2007/02/08

4

hyoshiokさん、ありがとうございます。いただいたエントリを読んで、オラクルでパフォーマンスチューニングとベンチマークに明け暮れていた頃を懐かしく思い出しました。。。今回の件は、アプリケーションレベルで取っていたログでプロファイリングを行って、問題点については正しく把握できたと考えています。これから着実に手を打っていきたいと思います。

  kenn on 2007/02/07

3

http://blog.miraclelinux.com/yume/2007/02/lingr_9ccf.html
ブログに書きました。釈迦に説法ですが何かの参考になればと思います。

  hyoshiok on 2007/02/06

2

Chat ServerはなるべくDumb Serverになるようにしています。ほぼすべてのロジックとデータ生成はRails側で処理し、Chat Serverは単なるコネクションパーキング兼土管。これは、同一のnotify電文をブロードキャストするだけでクライアントがChat Server Clusterのどのマシンにぶらさがっていようが届くようにするためで、現時点のアーキテクチャにおいてスケーラビリティを確保するための肝になっています。仮にJettyをちょっと賢くして全Roomオブジェクトを持たせたとしても、Eventテーブルを引っ張るには結局Rails側に行かないと取り出せないので、本質的な解決になりえないのです。残念ながら。

  kenn on 2007/02/06

1

読んでいて疑問に思った箇所があるので脊髄反射的なコメントです。
「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: 

このブログについて

ブロガープロフィール

アーカイブ

カテゴリ

ブログネットワーク

アルファブロガー

外資系エグゼクティブの日々I am Jamming!
外資系エグゼクティブの日々
村上敬亮 情報産業の未来図ネットワーク型産業構造への衣替え?
村上敬亮 情報産業の未来図
クロサカタツヤの情報通信インサイトグッバイ、レバレッジ!(1)
クロサカタツヤの情報通信インサイト
末吉隆彦 ロケーションウェアの「空」と「実」9月イベントお知らせ
末吉隆彦 ロケーションウェアの「空」と「実」
ケータイ時代のスタンダードiPhonista Nightの事後報告
ケータイ時代のスタンダード
江島健太郎 / Kenn's Clairvoyance新サービスをローンチしました
江島健太郎 / Kenn's Clairvoyance
鈴木健の天命反転生活日記パラレルワールドとしての電脳コイル
鈴木健の天命反転生活日記

読者ブロガー

IT業界(笑)最底辺層生活オトナになるということ
IT業界(笑)最底辺層生活
夢幻∞大のドリーミングメディア福祉国家の失敗〜40年前の「断絶の時代」を読む(3)
夢幻∞大のドリーミングメディア
Life with Mac and more.さあ来い!Silverlight 2
Life with Mac and more.
コミュニケンシュワ
コミュニケン

企画特集

ネットと家電をつなぐチャレンジ「Life-X」
第一題:ライフログ・シェアリングサービス「Life-X」の第一印象は?
エンタメCGM「gooメーカー☆メーカー」エンタメCGM「gooメーカー☆メーカー」
【第2回】メーカー/占いのコンテンツを作ってみた!

新着コメント

> まぁ、それはよいとして touch diamond はタッチとスタイラスが混在してお......
PCがメインだって?だったらiPhone買ってみな!
投稿者:kirifue
介護福祉を民営化したらいろいろ問題が起きたのでは? ともかく既得権益の病......
福祉国家の失敗〜40年前の「断絶の時代」を読む(3)
投稿者:porepore90
申し訳ありません、訂正をさせていただきます。 × 現在は既存メディアより......
東方なんとか、からSRCに辿り着く
投稿者:korly
行きたかったなー! 東京は、やはりー遠い!...
OSC2008Tokyo/Fallで勉強会大集合開催
投稿者:ヴィヴィ
アップルガイ=価値観のバイアス強すぎ!という印象バリバリでありましたが、......
PCがメインだって?だったらiPhone買ってみな!
投稿者:尊仁

ブログネットワークとは?

CNET Japan ブログネットワークは、元はCNET Japanの一読者であった読者ブロガーと、編集部の依頼により執筆されているアルファブロガーたちが、ブログを通じてオンタイムに批評や意見を発信する場である「オピニオンプレイス」、また、オピニオンを交換するブロガーたちが集うソサエティです。

広い視野と鋭い目を持ったブロガーたちが、今日のIT業界や製品に対するビジョンや見解について日々熱く語っています。

あなたもブログを書いてみませんか?

CNET Japanやその他サイトが提供するITニュースやコンテンツへの意見や分析、 ビジネスやテクノロジーに対するビジョンや見解について語っていただける方を 募集しています。ご応募はこちらから

ブログの投稿・管理

ブログの投稿はこちらから(※ブロガー専用)

ブログアワード2007開催決定!

今年最も活躍したブロガーを表彰します。詳細はこちらから

αマークって?

これは、CNET Japan 編集部の依頼に基づいて執筆されているCNET Japan アルファブロガーによるブログの印です。

Good!って?

CNET Japan ブログネットワーク内で拍手の代わりに使用する機能です。ブログを読んで、感激した・役に立ったなど、うれしいと思ったときにクリックしてください。多くGood!を獲得した記事は、より多くの人に読まれるように表示されます。

レビュー

今週の新製品総チェック:新PS3が登場!ニコンが発表した映像製品「UP」とは?
「東京ゲームショウ2008」が10月9日から開催され、新PS3やXboxの新作ゲームなど、ゲーム機の大型発表が相次
[レビュー]2011年画質を備えた高画質、多機能Blu-ray--ソニー「BDZ-X95」
ソニーのBlu-ray Discレコーダー新製品が登場した。2007年から引き継がれる「やりたいことから選ぶ」シリー
今週の新製品総チェック:よりモバイルPCとして進化した「Let's note」が登場
松下電器産業の「Let's note」、デルのデスクトップPCとPC新製品が数多く登場した。Let's noteは9時間駆動
今週の新製品総チェック:フルサイズCMOS搭載のキヤノン「EOS 5D Mark II」が登場
キヤノンからもフルサイズCMOSセンサを搭載した「EOS 5D Mark II」が登場した。合わせてコンパクトデジカメ
今週の新製品総チェック:第4世代iPod nano登場、ソニー「α」、松下「LUMIX」に新機種も