[連載2]どどんとふが重い?

前回の記事に引き続き、サーバー部分のどこが重たいのか、もう少し詳しく調べていきましょう。

サーバー内部の様子

クライアントとサーバーの間のネットワークを省略して、公式鯖のリクエストが処理される流れを図示したイメージがこちらです。

servermodel2

全体を書いても訳が分からないと思いますので、とりあえず、第壱鯖だけに絞って説明します。

青い矢印(A)がブラウザがindex.htmlやDodontoF.swfなどの静的コンテンツを読み込むリクエストの流れ。

緑の矢印(C)がFlashプラグインが静的コンテンツ(map、キャラクター、カットイン、ダイスなど)を読み込むリクエストの流れ。

赤い矢印(B)がチャットデータの送受信などを行う部分で、最も負荷が高い部分です。

このうちAとCの処理は、nginxだけでリクエストを返すので、応答時間としてはキャッシュが有効ならA-1の応答時間、キャッシュが無効でもA1とA-2の応答時間の合計となります。

赤い矢印の部分は、A-1とB-1とB-2の応答時間の合計となります。

Bの処理をさらに細かく見る(CGIとDodontoFServer.rb)

B-1とB-2は、レンタルサーバーなどでは、CGIとしてDodontoFServer.rbが動きますが、リクエストの都度、以下のような処理が動きます。

  1. ruby本体の読み込み
  2. DodontoFServer.rbのコンパイル
  3. DodontoFServer.rbが使用する周辺ライブラリの読み込み&コンパイル
  4. DodontoFServer.rbの処理実行

公式鯖のようなFastCGIでは、1~3は公式鯖起動時に一度実行されるだけで、リクエストの都度動くのは4だけです。
しかも、4の部分は常にリクエストが受け付けられるよう各鯖毎に60個のプロセスが常に待機しています。
公式鯖が大量人数を処理できる大きな理由がこれです。

逆に公式鯖がボトルネックになっている部分もここで、一つの鯖につき60個までしか同時にリクエストを処理することはできません。
例えば1回のリクエストを1秒で返したとしても、ログイン人数が120人を超えると応答時間の遅延や再接続が発生します。
実際には1秒もかかりませんが処理内容によって応答時間はまちまちなので、何人ならという指標は適切じゃないのですが、
次に説明するレイテンシの都合により、平均応答時間は0.3~1秒程度を要するめ、1鯖あたり200人超えたあたりから遅いと感じる人が出始めるようです。

帯域も大事だけどレイテンシ(応答時間)も重要

先ほどの「4.DodontoFServer.rbの処理実行」をさらに分解すると以下のようになります。

4.1: データ読み込み
4.2: データ(コマンド)の中身解析
4.3: 処理実行
4.4: 結果を出力

このうち4.2と4.3の部分は、公式鯖内部での処理になりますが4.2はただの構文解析なのでCPUしかほとんど使いません。

4.3でsaveDataディレクトリのファイルの読み書きやダイス判定を行うため、CPUとディスクIOを使います。このディスクIOに相当する部分がB-2となりますが、CPUに比べてディスクIOは非常に遅いため、公式鯖では高速化のためにRAMDISKを使っています。

そのため、HDDだとランダムIOの平均シークタイム10msec程度、SSDでも1~2msec程度の応答時間を10μsecのオーダーまで短縮させているのです。
で、問題は残りの4.1と4.4で行われる処理です。4.1と4.4のデータ送信は、A-1の通信も含むため、クライアントとの通信に時間がかかると通信待ちが生じてしまいます。
それによってCPUやディスクが空いているにも関わらず他のクライアントが待たされてしまい待たされた分のクライアントの応答時間が長くなってしまうのです。

レイテンシいろいろ

ではここで、A-1の通信にどれぐらいの時間がかかっているのかレイテンシを調べてみましょう。

まずは、いろんなところとPINGを打ってパケットの応答時間を調べてみます。(端数適当に切り捨て)

公式鯖管理人自宅⇔どどんとふ公式鯖 0.3ms
公式鯖管理人自宅⇔eo光トップページ 3.5ms
公式鯖管理人自宅⇔さくらインターネット 4ms
公式鯖管理人自宅⇔Googleトップページ 5ms
公式鯖管理人自宅⇔OCNトップページ 12ms
公式鯖管理人自宅⇔Yahooトップページ 15ms
公式鯖管理人自宅⇔こかげ工房さんの自宅鯖 18ms
モバイルルーター(mineo)⇔どどんとふ公式鯖 50ms

厳密には違うので異論は認めますが、応答時間×パケット数が、4.1と4.4のざっくりとした合計時間になります。(やっぱGoogleさんパネェっす。)
ICMPのパケットは56byteですしチェックサムの計算や上位アプリケーションレイヤーのハンドシェークにかかる時間も加わるので
実際の通信にかかる応答時間はもっと長くなります。

DodontoFServer.rbのCGIがクライアントに返す転送量を、Analogレポートからざっくり平均で調べてみると、
過去半年で800,661,712リクエストを、535GBのトラフィックを返しています。
ということは、1リクエストあたりの転送量は、平均して706Byteです。
Analogレポートのファイルサイズ毎のグラフで見ても、1KB未満のトラフィックが約90%ですので、妥当な線だと思います。

※DNSの名前解決は自分が契約しているISPのDNSやGooglePublicDNSでしょうし、一度解決した後はローカルのキャッシュにより
解決されますので、応答時間としてはもうちょっと短くなります。
参考:DNSとは
※SSL/TLSのハンドシェイクもHTTP KeepAliveを使えば、リクエストの都度ハンドシェイクをする必要はありませんが、
ある理由により公式鯖は無効にしています。
参考:SSL/TLSセッションの確立手順

仮に自分のPCとどどんとふ公式鯖の間のレイテンシが20msでハンドシェークに20パケットの往復が必要だとすれば、
DodontoFServer.rbのデータ通信1回には400ms(0.4秒)を要します。モバイル回線だと、1秒もかかってしまいます。

DodontoF.swfからDodontoFServer.rbに更新の有無を確認する頻度は2秒なので、ダイスや他の人の発言は
送信されてから画面の表示されるまでの間に0.4~2.4秒の遅れが発生します。

1300人が2.4秒間ずつ240個のプロセスを占有するリクエストを2秒毎に出したら、絶対詰まりますよね?

サーバーのCPUやディスクIOが暇なのに重いのはそれが理由です。

[連載2]どどんとふが重い?” への2件のフィードバック

  1. この話が真であるならば、nginxをもう1つ手前においてreverse proxyにし、セッションはnginx側で持ったほうがよさそうですね。

    1. コメントありがとうございます。本日の投稿で、それに近い解決策を記載いたしました。

コメントは受け付けていません。