質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.09%

Node.jsサーバーで1万の同時接続を処理したい

解決済

回答 3

投稿

  • 評価
  • クリップ 4
  • VIEW 4,568

score 25

Node.jsについて以前以下の質問をさせていただいた際に

将棋のようなターン性ゲームのサーバーをNode.jsで構築する際に
サーバープログラムを更新したいときに、現在ゲーム中のプレイヤーの処理を中断せず更新したい。

以下の回答を頂きました。

Node.jsはWebSocketのパイオニア的な存在なので、
凄まじいパフォーマンスを出しそうに見えますが、
コヤツは実は1台のマシンで1万の同時接続を捌けません。
1サーバに同時接続出来る人数を5000人とかに絞って、
縦割りで実装すれば今回の質問のような所まで問題を持っていけると思います。

ですが、そもそも別方向で考えれば質問文の疑問も一発解決出来る妙案があります。

フロントで動くWebSocketのAサーバはロジック部分を持たず、
ユーザとの通信のみに集中します。
つまり、本質問で懸念しているロジックを更新するためにプロセスを落としたら接続どうするねん問題から逃げます。

そしてサーバーサイドにもう一台、WebサーバもしくはWebSocketサーバを立ち上げます。
これをBサーバと名付けます。

Bサーバはロジック部分を受け持ちます。
ゲームの進行状況、対戦者の情報、スコア…といった状態も持たせておきます。

Aサーバからの接続を待ち受けて、リクエスト通りにゲームを進行させ、結果をAサーバに返します。
AサーバはBサーバにお伺いを建てながらプレイヤー達に結果を返します。
つまり、Bサーバはフロントからアクセス出来ないようにしつつ、1台サーバを噛ませる事でフロントからの接続を切らせずにバックエンドを更新する事が可能になるでしょう。

この場合、ゲームのアップデートの為にBサーバを落とすとなると、
プレイ中のデータが全て消えてしまいます。

なので極力Bサーバの変数領域やメモリ空間等にゲームのデータを持たせてはいけません。
RedisやMySQL等のデータベースに持たせるような作りにしましょう。

後はAサーバ→Bサーバへお伺いを立てるリクエストが失敗したら、
再度通信するような仕組みを作っておけばBサーバは更新し放題になるでしょう。

上記の回答を図にすると

[サーバー]-[フロント]

ではなく

[Bサーバー]-[Aサーバー]-[フロント]


とサーバーを噛ませることで、Bサーバーを更新してもフロントからの接続を切らないことが可能と理解しました。

しかし実際の通信量で考えると、毎回Aサーバー⇒Bサーバーに通信するため
[1]と[2]で通信料は変わらないと思うのですが
[2]の方法だと1万の同時接続は可能になるのでしょうか。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+2

前回の質問の流れですね。

[1]と[2]で通信料は変わらないと思うのですが

そうですね。
システムとクライアント間で発生するトラフィック量自体は変わりません。
むしろLAN内の通信が発生する分遅くなります。

[2]の方法だと1万の同時接続は可能になるのでしょうか。

根本のボトルネックがなんぞやが問題になるわけで、
ちゃんと理解・整理さえすれば解決します。

Aサーバが1万クライアントを捌けないという根拠は
単純にNode.jsという言語の限界の話です。
コネクションを繋ぎっぱなしにしてどうこうする為、1台で解決させようとするとメッセージの取りこぼし等が発生します。

Node.jsはシングルスレッドで動作する言語なので、
いくらlibuvライブラリが優れているとはいえ、
イベントループ頼みで毎秒何万関数を実行するようなケースではパンクしてしまいます。


Aサーバの解決策としては

例えばElixir+PhoenixのWebScoket通信は数万クライアントを捌けるそうなので、
もっと得意な言語に頼るというのは解決策の一つです。
これはElixirのスレッドを作成するコストが非常に安いという特徴が良い方向にあらわれています。

普通に対処するならWebSocketで通信を行うサーバーの複数台構成ですかね?
ロードバランシングみたいなのも必要になると思いますが、
WebSocketのロードバランシングは詳しくないので技術的な調査を行いながら手探りで実装していく形になるでしょう。

また、複数台構成にするのであれば、状態は出来る限り持たない方が良いですね。
この辺は前回お話した内容の通りです。


もちろんAサーバにクライアントが押し寄せる場合、
Bサーバも不安になることでしょう。

ですが、内部的にAサーバ群からしかアクセスが出来ないだけで、
これは普通のWebサーバと同じです。
少ない台数で頑張るならScalaやElixir、Go等の言語へ移行すれば良いですし、
儲けが出るのであれば台数を増やして対応すれば良いでしょう。

最悪Aサーバと同じ台数分Bサーバを縦割りで用意して、
A1サーバは常に決め打ちでB1サーバに問い合わせるようにしておけばBサーバの負荷という面では問題になりえません。
(金銭面の問題はあるかもしれません)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/14 01:27

    前回に引き続きご回答ありがとうございます。

    >コネクションを繋ぎっぱなしにしてどうこうする為、1台で解決させようとするとメッセージの取りこぼし等が発生します。

    ここをもう少し詳しく教えて頂きたいのですが、
    TCP通信の場合はパケットロストした場合に再送処理が数回行われると理解しています。

    そのTCP通信でメッセージの取りこぼしが起こる理由は
    接続数が増える⇒通信が増えパフォーマンスが低下する
    ⇒パケットロスが増える⇒再送回数を超えるロス⇒取りこぼし
    ということでしょうか。

    >根本のボトルネックがなんぞやが問題になるわけで、ちゃんと理解・整理さえすれば解決します。
    サーバー負荷を軽減できれば大量クライアントを捌けるが、
    1台のマシンで実行できる処理の数には限界はある。
    処理が重ければ重いほど捌けるクライアント数は減る。

    という理解で間違っていないでしょうか。

    キャンセル

+1

[1]と[2]で通信料は変わらないと思うのですが

データ量は変わらないかもしれませんが、通信は大きく違ってきます。

「1万ユーザーに1つずつデータを送る」より「1つの接続で1万のデータを送る」ほうが、負荷は圧倒的に軽いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/13 13:03

    [2]の方法だと1万の同時接続は可能になるのでしょうか。>1万の同時接続が「不要になる」のがメリットです(A~Bは繋ぎっぱなしでも大丈夫なので、接続数的にはごくわずかとなります)。

    キャンセル

  • 2019/03/13 13:10

    実際、WebサーバとDBサーバの間は「持続的接続」といってコネクションをできるだけ張ったままにするのが一般的です。

    キャンセル

  • 2019/03/14 00:49

    回答ありがとうございます。
    以下のような考え方で正しいでしょうか。

    [Bサーバー]:Aサーバーが接続するサーバー。
    データベースへのアクセスや、ゲームのロジック部分の処理を受け持つ。
    通信はAサーバーだけと行えばよいため、上記の処理に集中できる。
    データベースやソケットの接続数がわずかとなるため負荷が軽い。

    [Aサーバー]:フロントが接続するサーバー。
    フロントが多数接続してくる。
    ソケットの接続数が大量になるため負荷がかかる。
    実際のロジック処理などをBサーバーに任せることで負荷を軽減し
    通信処理に集中することができる。

    簡潔に言えば役割分担ということでしょうか。

    キャンセル

+1

1万の同時接続の要件って、かなり重い設計作業になると思いますよ。
素人の手の出せる範囲ではないです。

負荷軽減策や処理の分散等、考慮範囲は多岐に渡るので、アプリケーション要件を整理して専門家に相談したほうが良いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/14 00:36

    回答ありがとうございます。
    負荷軽減策や処理分散など、考慮すべきことが多いことは理解しています。
    その上で自分でも勉強したいと思い質問させていただきました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.09%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る