web-dev-qa-db-ja.com

システム設計:スケーラブルなチャットサーバー

次の要件を備えたスケーラブルなチャットサーバーを設計するように求められたとします。

  1. 主な使用例は、プレーヤーAがBをオンラインで見て、AがBにメッセージを送信し、Bがそれを受信することです。
  2. 二次的な使用例は、プレーヤーAがBofflineを確認し、AがBにメッセージを送信します。Bがオンラインに戻ると、Bがメッセージを受信します。 (プッシュ通知なし)。
  3. 目標は、待ち時間を最小限に抑えることです。スピードが重要です。
  4. メッセージは順番に到着するはずです。メッセージを失うことはありませんが、たまに重複を受信することは問題ありません。
  5. テキストデータのみで、バイナリデータはありません。
  6. チャットの履歴を保存する必要はありません。送信されると、メッセージは破棄されます。

私はこの記事を読んでいます: League of Legendsはチャットを7000万人のプレーヤーにスケーリングしました そして、彼らがゲームで使用したコアアーキテクチャを逃したと思います。しかし、とにかくここに私の「思考プロセス」があります。誰かがそれを見ることができますか?

  • 二次的なユースケースが存在しなければ、何も保存する必要はありません。私はp2pネットワークを使用できると思います。この場合、ユーザーは定期的にpingメッセージを "i'on online"をすべての友達に送信してプレゼンスを通知します。
  • しかし、後でメッセージを配信できるようにメッセージを保存する必要があるため、ユーザープレゼンス、ユーザーフレンドシップリスト、メッセージを保存する独自のサーバーが必要です。
  • サーバーをユーザーの近くに配置することで、待ち時間を最小限に抑えるという目標を達成できます。つまり、複数のサーバーが存在するため、同期を維持する必要があります。また、1つのサーバーがすべてを格納しないように、それらの負荷を分散する必要があります。
  • インターネットのどこかで、サーバーの負荷を分散する方法は各ユーザーにサーバーを割り当てることだと読んだことがあります。たとえば、サーバー1にはユーザーAに関連するすべてが割り当てられ、サーバー2にはユーザーBに関連するすべてが割り当てられます。これは、近接度によって決定できます。
  • Aが何かをBに送信する場合、メッセージをサーバー2にディスパッチする方法が必要です。サーバーとの通信にサービスバスを使用する場合があります。
  • したがって、フローは次のようになります。

    1. Aさんが「こんにちは!
    2. サーバー1はメッセージとBを受信します。ユーザーベースでBが見つからないため、サーバー1はメッセージをサービスバスに転送します。メッセージのコピーを保存します。
    3. サービスバスは、すべてのサーバーにユーザーBを探すように要求します。
    4. サーバー2は、ユーザーベースにBがあると返信します。
    5. サーバー2はメッセージを受信して​​保存します。
    6. サーバー2はユーザーBにメッセージを送信します。
    7. サーバー2は、メッセージが送信されたことをサービスバスに通知します。彼はメッセージを破壊します。
    8. サーバー1はメッセージのコピーを破棄します。
  • Bがオフラインの場合、ステップ5まではすべて同じままです。違いは、サーバー1はメッセージのコピーを破棄できますが、サーバー2は破棄できないことです。

  • さて、ストレージ...私の推測では、各サーバーに独自の永続ストレージが必要ですが、ここで何を最適化すればよいかわかりません(読み取り速度?書き込み速度?)。また、MySQLストアとNoSQLストアのどちらが優れているかわかりません。 NoSQLはパーティション分割するように最適化されており、ここでは必要がないため、MySQLで十分だと思います。
  • サーバーがクラッシュした場合、迅速にフェイルオーバーする方法が必要です。 「プライマリ」および「セカンダリ」サーバーのように各場所に配置でき、プライマリをプライマリストレージに接続し、セカンダリを複製データに接続するとします。

したがって、全体的なアーキテクチャは次のようになります。

architecture

私は多くのことをここで見逃していることに気づきました、私は明白な何かを見逃しましたか?私の思考プロセスの一部がまったく間違っているのですか?

あなたはcan P2Pネットワークを使用しますが、アーキテクチャ的に興味深いものです。

ピア検出のDHTとしてKademliaのようなものを使用することは、ターゲットに到達する前に限られた数のノードと通信することを意味します。これらの各ホップにメッセージを保存した場合、メッセージストアに冗長性があり、要件に対して十分に信頼できる可能性があります。オフライン配信は、バッファリングされた各メッセージの定期的な転送試行を意味します。 ピアの発見の観点から、かなり低いレイテンシが保証されます。これは、おそらく問題の中で最もコストのかかる部分です。

直接P2P接続が確立されると、明らかにオンラインモードになり、オフラインストレージをスキップできます(またはスキップできません)。

メッセージを永続的に格納するノードを実行することもできますが、それ以外の場合は通常のDHT参加者として機能します。少数のノードを実行するだけで、より高い信頼性が得られます。

しかし、@ aridlehooverが書いているように、実際には最終的な回答を提供できないほど多くの可能な回答があります。

1

私はこれをクラウドで設計し、MongoDBやAzure Cosmos DBなどを使用します。良い点は、データベースがデータ側のスケーリングを処理するため、心配する必要がないことです。

あとは、選択したデータストアの上にWeb APIを作成するだけです。これはクラウドベースの場合もあるので、クラウドプロバイダーはリクエストのスケーリングを自動的に処理できます。

何らかの理由でクラウドが選択肢にならない場合(現時点では考えられません)、これらのデータベースプラットフォームはどちらもオンプレミスでもホスト可能であることを認識してください。これにより、コンテナーやKubernetesなどで簡単に実行できるWeb APIのスケーリングが可能になります。これは、データベースプラットフォーム自体をホストするための推奨事項でもあります。