web-dev-qa-db-ja.com

nodejsアプリをスケーリングするための最良の方法は何ですか?

基礎

現在、私の友人の何人かと私はnodejsで作られたブラウザゲームを開発しようとしています。これはマルチプレイヤーのトップダウンシューティングゲームであり、クライアント側とサーバー側の両方のコードのほとんどはjavascriptです。私たちは、私たちが入りたいと思う良い一般的な方向性を持っており、ゲームの開発をとても楽しんでいます。このゲームを作るときの私たちの目標の1つは、チートをできるだけ難しくすることでした。そうすれば、すべてのゲームロジックがサーバー側で処理されます。クライアントは入力をWebソケット経由でサーバーに送信するだけで、サーバーはゲームで起こっていることでクライアント(Webソケットも)を更新します。これが私たちの問題の始まりです。

サーバー側の計算はすべてかなり重くなり、10人を超えるプレーヤーを処理するために何らかの方法でスケーリングする必要があることがわかりました(もっと多くのプレーヤーをホストできるようにしたい)。最初は、必要に応じて垂直方向にスケーリングできると考えていましたが、nodejsはシングルスレッドであるため、1つのコアしか利用できません。つまり、より強力なサーバーを入手しても、その問題は解決されません。私たちの唯一の解決策は、水平方向にスケーリングすることです。

ここで質問する理由

Nodejsゲームをスケールアウトする方法の良い例を見つけることができませんでした。私たちのユースケースはかなり特殊であり、これを自分たちで行うために最善を尽くしましたが、外部の意見やアドバイスから本当に恩恵を受けることができました

詳細

この問題を解決する方法については、すでに多くのことを考えています。私たちは一週間以上それに取り組んできました。これまでにまとめたものは次のとおりです。

4種類のサーバー

タスクを4つの異なる「タイプ」のサーバーに分割しています。それぞれが完了する特定のタスクがあります。

プロキシサーバー

プロキシサーバーはスタック全体の最前面に配置され、インターネットから直接アクセスできる唯一のサーバーになります(これらのサーバーがさらに存在する可能性があります)。 haproxyがあり、すべての接続をWebサーバーにルーティングします。豊富な機能セット、信頼性、およびほぼ無敵の速度のために、haproxyを選択しました。

Webサーバー

Webサーバーは、Web要求を受信し、すべてのWebページを提供します。また、ロビーの作成/管理とゲームの作成/管理も処理します。これを行うために、彼らはゲームサーバーにそれが持っているロビー、そのロビーにいるユーザー、そして彼らがプレイしようとしているゲームについての情報を伝えます。次に、Webサーバーはユーザー入力についてゲームサーバーを更新し、ゲームサーバーはゲームで何が起こっているかについてWebサーバーを更新します(クライアントはクライアントを更新します)。 WebサーバーはTCPソケットを使用して、あらゆるタイプの管理についてゲームサーバーと通信し、ゲームの更新について通信するときにUDPソケットを使用します。これはすべてnodejsを使用して行われます。

ゲームサーバー

ゲームサーバーは、ゲームに関するすべてのゲーム計算と変数の更新を処理します。ゲームサーバーはdbサーバーとも通信して、ゲーム内のプレーヤーに関するクールな統計を記録します。これはnodejsで行われます。

データベースサーバー

Dbサーバーはデータベースをホストします。この部分は、これまでで最もクールなdbである rethinkdb を見つけたので、実際に最も簡単であることがわかりました。これは簡単に、そして奇妙なことに、アプリケーションのスケーリングの最も簡単な部分であることがわかりました。

その他の詳細

あなたが私たちの全体の起き上がりに頭を悩ませているなら、 これを見てください 、それは私たちがどのようにスケーリングすると思うかについての半正確なチャートです。

興味がある場合、またはゲームを見ると役立つと思われる場合は、現在、ここではスケーリングされていない状態でホストされています。

私たちが望まないいくつかのこと

  • Nodejsのクラスターモジュールは使いたくありません。安定しておらず( ここ )、他のサーバーには拡張されず、他のプロセッサにのみ拡張されます。水平スケーリングに飛躍したいと思います。

私たちの質問、要約

私たちは正しい方向に進んでいることを願っており、宿題を終えましたが、確かではありません。これを正しい方法で行う方法について、確かにいくつかのヒントを得ることができます。

ありがとう

これはかなり長い質問だと思いますし、よく考えられた答えを出すのは簡単ではありませんが、本当にありがたいです。

ありがとう!

24
Michael

あなたのケースについての私の自発的な考えに従って:

マルチコアの使用法

node.jsは、複数のコアで拡張することもできます。どのように、あなたは読むことができます 例えばここにまたは単にそれについて考える:あなたは1つのコアで実行されている1つのスレッド/プロセスを持っています、あなたは複数のコアを使用するために何が必要ですか? ?複数のスレッドまたは複数のプロセス。メインスレッドから他のスレッドまたはプロセスに作業をプッシュすれば完了です)。

個人的には、複数のコアを使わないアプリケーションを開発するのは幼稚だと思います。いくつかのバックグラウンドプロセスを利用する場合は問題ありませんが、これまでnode.jsメインイベントループでのみ作業を行う場合は、間違いなく投資する必要がありますアプリをコア上でスケーラブルにするための時間。

ちなみに、IPCのようなものを実装するのはそれほど簡単ではありません。できますが、ケースが複雑な場合は、クラスターモジュールを使用することをお勧めします。これは明らかにお気に入りではありませんが、何かが「実験的」と呼ばれているからといって、それがごちゃごちゃしているという意味ではありません。試してみてください。途中でモジュールのバグを修正することもできます。複雑な問題には、広く使用されているソフトウェアを使用する方がよいでしょう。新しいホイールを発明します。

また、(まだ行っていない場合は)nextTick機能の(賢明な)使用法についても検討する必要があります。これにより、メインイベントループでCPUを集中的に使用するタスクを一時停止し、その間に他の作業を実行できます。あなたはそれについて読むことができます 例えばここで

計算に関する一般的な考え

あなたは間違いなくゲームエンジンのあなたのアルゴリズムを非常によく見るべきです。これが現在のボトルネックであり、実際にはほとんどすべてのゲームで計算が最も重要な部分であることにすでに気づいています。スケーリングはこの問題を1つの方法で解決しますが、スケーリングは他の問題を引き起こします。また、すべてに問題解決者として「スケーリング」を投げて、すべての問題が消えることを期待することはできません。

最善の策は、ゲームコードをエレガントで高速にすることです。問題を効率的に解決する方法を考えてください。 Javascriptで何かを効率的に解決できないが、問題を簡単に抽出できる場合は、代わりに小さなCコンポーネントを作成してみませんか?これも個別のプロセスとしてカウントされ、メインnode.jsイベントループの負荷が軽減されます。

プロキシ?

個人的には、現時点ではプロキシレベルの利点はわかりません。あまり多くのユーザーを期待していないようですので、CDNの解決などの問題を解決する必要はありません...考えても大丈夫ですが、今はあまり時間をかけません。

技術的には、Webサーバーソフトウェアがとにかくプロキシ機能を提供する可能性が高いです。ですから、紙に書いておいても大丈夫ですが、今は専用のハードウェアを使うつもりはありません。

エピローグ

残りは多かれ少なかれ私には問題ないようです。

12
GhostGambler

ゲームに少し遅れていますが、ここを見てください: http://goldfirestudios.com/blog/136/Horizo​​ntally-Scaling-Node.js-and-WebSockets-with-Redis

メモリ管理とは何の関係もありませんでした。ご存知のように、nodejsはそのメモリを他のプロセスと共有しないため、スケーリングする場合はインメモリデータベースが必須です。 (RedisMemcacheなど)。 redisからの着信リクエストを受け入れるには、各ノードでパブリッシャーとサブスクライバーeventを設定する必要があります。このようにして、(HAProxyの前に)x niloのサーバーをスケールアップし、redisからパイプされたデータを利用できます。

このnodeアドオンもあります: http://blog.varunajayasiri.com/shared-memory-with-nodejs これにより、プロセス間でメモリを共有できますが、Linuxでのみ機能します。これは、ローカルプロセス間で常にデータを送信したくない場合、またはnodes ipcapiを処理する必要がある場合に役立ちます。

node内で子プロセスをフォークして、新しいv8分離を実行し、高価なCPUバインドタスクを支援することもできます。たとえば、プレイヤーは私のアクションRPGゲーム内でモンスターを殺し、かなりの戦利品を手に入れることができます。私にはLootGeneraterという子プロセスがあり、基本的にプレーヤーがモンスターを殺すたびにゲームを送信しますidmob_id、およびuser_idデフォルトを介してプロセスにIPC api .send。子プロセスはそれを受け取ると、大きな戦利品テーブルを反復処理してアイテムを管理し(redisなどに格納)、パイプで戻します。

これにより、イベントループが大幅に解放され、拡張に役立つと私が考えることができるアイデアが1つだけあります。ただし、最も重要なことは、インメモリデータベースシステムを使用し、使用するデータベースシステムを中心にゲームコードアーキテクチャが設計されていることを確認することです。今私がした間違いをしないでください今すべてを書き直さなければなりません:)

お役に立てれば!

注:Memcacheを使用することにした場合は、別のpub/subシステムを利用する必要があります。

1
NiCk Newman