私たちが直面している問題
初期業務の主なシーンは、ライブ ブロードキャスト ルームのグループ チャット メッセージと、シングル チャット メッセージの一部です。教育シナリオであるため、チャット ルームを分割する際に業務をクラスに分け、各チャット ルームの人数は 500 人であると仮定します。
問題 1: ユーザーのメンテナンス
ライブ ブロードキャスト シナリオのグループ チャットと WeChat などの一般的なグループ チャットとの間には、ユーザーのメンテナンスに大きな違いがあります。WeChat のグループ ユーザー関係は比較的固定されており、ユーザーがグループに出入りする操作は比較的まれであり、ユーザー セットも比較的固定されています。ライブ ブロードキャスト ルームのユーザーは非常に頻繁に出入りし、ライブ ブロードキャスト ルームは時間に敏感です。ライブ ブロードキャスト ルームへの入退室の実際のピーク値はQPS
10,000 を超えないため、Redis
チャット ルーム ユーザー リストの保存と有効期限の問題を解決できます。
質問 2: メッセージの転送
500 人のチャット ルームのすべてのユーザーが同時にメッセージを送信すると、メッセージ転送量はQPS
500*500=2.5w になります。ライブ ブロードキャスト クライアントの観点から:
リアルタイム : メッセージ サービスがピーク除去処理を実行する場合、ピーク メッセージの蓄積によりメッセージ遅延が増加します。また、一部のシグナリング メッセージは時間に依存するため、遅延が大きすぎるとユーザー エクスペリエンスとリアルタイム インタラクションに影響します。
ユーザー エクスペリエンス: 端末にはさまざまなユーザー チャットや信号メッセージが表示されます。通常、1 画面あたり 10 ~ 20 件を超えるメッセージは表示されません。1 秒あたり 20 件を超えるメッセージが送信されると、画面が連続的にスワイプされる現象が発生します。また、大量のメッセージが端末に送信されるため、継続的な高負荷が発生します。
そこで、メッセージにさまざまな優先順位を定義します。優先度の高いメッセージが最初に転送され、破棄されないことが保証されます。優先度の低いメッセージは、特定の破棄戦略の後に転送されます。
質問 3: 歴史的なニュース
ビジネス面では、リプレイビデオを生成し、履歴シグナリングや対話型チャットなどの情報を取得する必要があります。メッセージ転送の適時性を確保するには、履歴メッセージを迅速に作成できる必要があります。
メッセージのストレージには主に、書き込み拡散と読み取り拡散の 2 つのカテゴリが含まれます。メッセージの保存容量と保存時間を削減できる読み取り拡散方式を採用しています。再生の優先度は高くないことを考慮して、ストレージコンポーネントを選択することにしましたPika
。学習コストと開発コストを削減できるのは、Pika
インターフェイスと類似性です。Redis
同時に、追記方式を採用しているため、書き込みパフォーマンスは同等ですRedis
。
質問 4: メッセージ シーケンス
シグナリング メッセージの順序の要件では、同じ人がメッセージを送信する順序と、同じチャット ルーム内のユーザーがメッセージを受信する順序が同じであることを保証する必要があります。
メッセージの順序の解決は、Kafka
などのキューを使用することで保証できますが、Kafka
ある程度の遅延が発生します。遅延を軽減するために、コンシステント ハッシュ戦略を使用してメッセージ転送を処理します。これについては後で詳しく説明します。
設計目標
安定した効率的なメッセージ通信サーバーを作成します。
- 高信頼性、高安定性、高性能の長期接続サービスを提供します。
- 同時に何百万もの長時間のオンライン接続をサポートします。
- マルチクラスターの迅速な展開と拡張をサポートします。
安定性と拡張性
安定性とスケーラビリティはアーキテクチャ全体の非常に重要な部分であり、メッセージ サービスの主な戦略は动态配置
統合で策略下发
あり、複数のコンピュータ ルームの動的スケジューリング、自動障害転送を効果的に実現でき、水平拡張機能も備えています。
SDK
構成:再試行戦略、タイムアウト時間、ビジネス スケジューリング アドレスなどのコア パラメーターなど、サーバーに接続する前に各エンドで取得される共通の構成情報- スケジューリング: さまざまなビジネス タイプとユーザー情報に応じて、スケジューリング アドレスを要求して、現在のサービスの接続アドレスを取得します。
- 接続: ターゲットのサービスクラスターへの接続を開始します。
- フェイルオーバー: サービスの途中で問題が発生し、接続が切断された場合、スケジューリングが再度開始され、バックエンド クラスターが利用可能なクラスターを再計算し、利用可能なクラスター ノードにユーザーをスケジュールします。
- 切断にはさまざまな理由が考えられますが、一般的な理由は次のとおりです。
- ネットワークが不安定であるため、ネットワーク パケット損失が発生し、ハートビート タイムアウトがトリガーされます。
- クライアントとサーバーで例外が発生し、接続がアクティブに閉じられます。
- 構成およびスケジューリング インターフェイスの高可用性レベルでは、マルチアドレス ポーリング、複数の再試行、
CDN
静的リソース カバレッジなどの戦略が使用され、
最初のステップで構成サービスでスケジュール アドレスをまとめて返し、それを 2 つのステップに分割するのはなぜではないかと疑問に思われるはずです。これは実際には、メッセージ サービスがマルチサービスの同時実行性をサポートし、構成サービスはSDK
初期化中に 1 回だけ取得され、 SDK
コア制御パラメーターが取得されることを考慮しています。後続のさまざまなサービスは、さまざまな時点で独自のスケジューリング アドレスを動的に取得できるため、サービスが相互に影響を与えないよう、サービスの分離性と安定性を効果的に向上できます。
安全性
セキュリティと攻撃対策、メッセージ内容の暗号化、アカウントのセキュリティ認証に関して一連の対策が講じられており、現在使用している方法は次のとおりです。
- 各ビジネス パーティに
App
発行されたAppKey
署名は、安全でないトラフィックがスケジューリング アドレスを取得するのを防ぐため、または少なくともアドレスをApp
取得するために防御を突破する必要があるように、スケジューリング プロセス中に検証されます。 - ディスパッチャーによって取得されたアクセスアドレスは合法であり
Token
、サーバー モジュールは犯罪者による投機を回避し、クラッキングコストを増加させるために、ユーザーのアカウントシステムと同様に適時性と合法性をAuth
検証します。Token
- コンテンツ暗号化機能は動的に有効にすることができ、コンテンツ暗号化送信用にユーザーの独立した対称暗号化
AES
キーが取得されます。 - データ転送の使用
TLS
/SSL
トンネル暗号化 IDC
コンピューター室の保護、クラウド ベンダーによるインテリジェントな保護など、安全なネットワーク環境を使用します 。
接続の安定性
実際の複雑なネットワーク環境では、ネットワークの安定性が低下したり、パケット損失が発生したり、頻繁に切断されたり、接続の確立に完全に失敗したりする非常に少数のユーザーが存在する可能性があります。一方では、クライアントのネットワーク ステータス ログを収集してユーザーを診断し、他方では、このラスト ワンマイルの問題に対してネットワークを高速化します。
クラウド ベンダーの TCP
高速化を利用してWebsocket
、一部のエッジ ユーザーの接続問題を解決する
自社構築のエッジノードをより細かく制御可能
海外ネットワーク高速化、複数クラウドサービスプロバイダーとのドッキング
信頼性と一貫性
メッセージ サービスの中核となる保証として、主にサーバーとクライアントのアーキテクチャにそれぞれ導入され、主な保証戦略は次のとおりです。
- メッセージの厳密な確認メカニズムにより、クライアントからサーバーに送信されたメッセージは、確認されている限りディスクに保存されるため、アップリンク メッセージは失われず、ダウンリンク メッセージは再試行できます。その後も何度か
- クライアント障害の再送信、ソート、タイムアウトなどの戦略は主に
TCP
スライディング ウィンドウのアイデアを指します。これにより、確認されたメッセージのシーケンスをビジネス層に返すことができ、順序が乱れたメッセージは戦略に従ってタイムアウトを待つ必要があります。 - サーバー側の重複送信および再送信防止戦略
ID
一意性と順序を保証する、グローバルに一意なメッセージ とローカルに順序付けされたシーケンス番号
サーバーメッセージIDの設計
メッセージ ID
の設計は、メッセージの信頼性と一貫性にとって非常に重要です。一方で、メッセージの一意性を保証するために、すべてのメッセージを追跡およびクエリできることを保証する必要があります。メッセージの順序を保証するために必要です。
メッセージ サービス生成 ID
の唯一のアルゴリズムは Twitter
スノーフレーク アルゴリズム に基づいておりSnowflake
、サービスに合わせていくつかの微調整が行われます。
ローカルに順序付けされたシーケンス ID
順序立てて ID
、グループチャットやグループチャットなどのセッションごとに独立したシリアル番号を生成するいくつかの革新的な設計を採用しています。相互影響がないため、シリアル番号発行パフォーマンスが大幅に向上します。シーケンス番号の主な目的は、メッセージのローカルな順序を保証することです。では、信頼性と一貫性を実現するためにサーバーは何を行ったのでしょうか? まず、メッセージ構造を再設計しました。新しいメッセージ構造は次のとおりです。
新しいメッセージ構造は、一方向リンク リストに似ています。従来の一方向リンク リストとの違いは、リンク リストが逆の順序であり、各メッセージに前のメッセージ、このメッセージ、およびグローバル メッセージがある SeqId
こと SeqId
です Id
(メッセージ追跡に使用されます)。メッセージは SeqId
すべてチャット オブジェクトを対象としています。たとえば、チャット ルーム A
とチャット ルームの B
メッセージは SeqId
互いに独立しています。
ここでプリオーダーを使用する際の 主な考慮事項ID
は、単調増加シーケンスを使用してメッセージの信頼できる一貫性を確保できることです。ここでは連続的な単調増加は必要ないことに注意してください。たとえば、図内のシリアル番号は、101
、102
、104
、です107
。これをサポートするには、大量の高性能をサポートするいくつかのシリアル番号生成サービスを使用し、番号セグメントを事前に割り当てる戦略を使用してパフォーマンスを向上させることができます。興味があれば、より成熟したソリューションをオンラインで確認できます。もちろん、メッセージのサイズが大きくない場合は、単純かつ乱暴に使用して、 Redis
増加し続けるシーケンス番号を生成することができます。
メッセージの信頼性と一貫性の保証
メッセージの信頼性と一貫性は、主に次の側面に反映されます。
- 端末で受信したメッセージは失われず、繰り返されず、順序が狂うこともありません
。上記の導入によりSeqId
、クライアントはメッセージを並べ替えて空のロジックを判断できます。具体的なプロセスは次のとおりです:
- 同じ部屋にいるユーザーには同じ順序でメッセージが表示されます
- ユーザーが送信した注文はユーザーが受け取った注文と同じです
後者の 2 つの機能については、サーバーは主に同じ送信者からのメッセージを Hash
同じスレッドで処理し、同じルーム内のメッセージも Hash
同じスレッドで処理します。
クライアントがメッセージの送信に失敗した場合、メッセージの信頼性はどのようにして保証できるのでしょうか?
再送信が確実に送信され、メッセージが繰り返されないようにするには、次の 2 つのことを行います。
-
まず最初に、シリアル番号も導入します。このシリアル番号はユーザー レベルにあります。ユーザーはオンライン期間中に 1 つのシリアル番号のみを維持します。このシリアル番号は単調増加です。クライアントは送信に失敗すると、一定回数の再送信を試み、
N
すべての再送信に失敗した場合はコールバックしてビジネス層に通知します。クライアントが再送信してもシーケンス番号は単調増加するため変化しないため、サーバはシーケンスが繰り返されているかどうかで再送メッセージかどうかを判別できる。 -
次に、サーバーはメッセージ送信結果を一定期間キャッシュし、再送と判断した場合には、前回の送信結果を問い合わせてクライアントに直接結果を返します。全体的なプロセスは次のとおりです。
ユーザーは全国に散らばっており、ユーザーごとにネットワークの状態が異なり、品質も大きく異なるため、 TCP
長期にわたる接続の切断は正常です。通常、切断から再接続までの間、サーバーはユーザーにメッセージを転送できないため、次の図に示すプロセスを使用して、再接続後に失われたメッセージをユーザーが確実に受信できるようにします。