分散IM(インスタントメッセージング)システムを自分で構築する

分散IM(インスタントメッセージング)システムを自分で構築する

以前、「*** のメッセージ プッシュ システムの設計」という記事を共有しました。記事にはいくつかの疑似コードが掲載されていますが、実行可能なソースコードを直接共有したいと考えている友人もいます。長い間経ちましたので、その穴を埋める時が来ました。

そこで、以前の内容に基づいていくつかの内容を改善しました。このプロジェクトの紹介を見てみましょう。CIM (CROSS-IM) は開発者向けの IM (インスタント メッセージング) システムです。また、開発者が独自の水平スケーラブルな IM を構築するのに役立つコンポーネントもいくつか提供します。

CIM を使用すると、次の要件を実現できます。

  • IM インスタント メッセージング システム。
  • アプリ用のメッセージ プッシュ ミドルウェア。
  • IoT 大量接続シナリオにおけるメッセージ透過伝送ミドルウェア。

完全なソースコードは GitHub でホストされています:

  1. https://github.com/crossoverJie/cim

今回は主に IM インスタント メッセージングに関するものなので、特別に 2 つのビデオ デモ (グループ チャットとプライベート チャット) を録画しました。

グループチャット

プライベートチャット

建築デザイン

具体的なアーキテクチャ設計を見てみましょう。

  • CIM の各コンポーネントは Spring Boot を使用して構築されます。
  • 基盤となる通信を構築するには、Netty + Google Protocol Buffer が使用されます。
  • Redis は各クライアントのルーティング情報、アカウント情報、オンライン ステータスなどを保存します。
  • Zookeeper は、IM サーバー サービスの登録と検出に使用されます。

システム全体は主に以下のモジュールで構成されています。

  • cim-server、IM サーバー: クライアント接続の受信、メッセージの透過的な送信、メッセージのプッシュなどの機能に使用されます。クラスターの展開をサポートします。
  • cim-forward-route、メッセージ ルーティング サーバー: メッセージ ルーティング、メッセージ転送、ユーザー ログイン、ユーザー オフライン、およびいくつかの操作ツール (オンライン ユーザー数の取得など) を処理するために使用されます。
  • cim-client、IM クライアント: 1 つのコマンドで起動して他のユーザーと通信 (グループ チャット、プライベート チャット) できるユーザー用のメッセージング ターミナル。よく使用されるコマンドがいくつか組み込まれており、簡単に使用できます。

フローチャート

全体的なプロセスは比較的単純で、フローチャートは次のとおりです。

  • クライアントはルートへのログインを開始します。
  • ログインが成功すると、Zookeeper から利用可能な im-server が選択されてクライアントに返され、ログイン情報とルーティング情報が Redis に保存されます。
  • クライアントは IM サーバーへの長い接続を開始し、成功した後ハートビートを維持します。
  • クライアントがオフラインになると、ステータス情報は Route を通じてクリアされます。

したがって、自分でデプロイする場合は、次の手順に従う必要があります。

  • 基本的なミドルウェア Redis と Zookeeper を構築します。
  • 実際の IM サーバーである cim-server をデプロイします。パフォーマンス要件を満たすために、水平拡張をサポートしており、同じ Zookeeper に登録するだけで済みます。
  • ルーティング サーバーである cim-forward-route をデプロイし、すべてのメッセージがそれを通過する必要があります。ステートレスなので、可用性を向上させるために Nginx プロキシとして使用することもできます。
  • cim-client は真のユーザー指向クライアントです。起動後、自動的に IM サーバーに接続し、コンソールでメッセージを送受信できるようになります。

詳しい使用方法については、クイックスタートを参照してください。

詳細設計

次に、グループチャットやプライベートチャットのメッセージの流れなど、具体的な実装に焦点を当てます。 IM サーバーの負荷分散。サービスが登録および検出される方法など。

IM サーバー

まずサーバーを見てみましょう。主にクライアントのオンラインとオフライン、メッセージ送信などの機能を実装します。

まず、サービスを開始します。

Spring Boot で構築されているため、アプリケーションの起動時に Netty サービスを開始する必要があります。

Pipline からは、Protobuf エンコードとデコードが使用されていることがわかります (特定のメッセージがクライアントで分析されます)。

登録の検出

IM サーバーの水平拡張要件を満たす必要があるため、cim-server は独自のデータを登録センターに公開する必要があります。

したがって、アプリケーションが正常に起動した後、そのアプリケーション独自のデータを Zookeeper に登録する必要があります。

主な目的は、現在のアプリケーションの IP + CIM サーバー ポート + http ポートを登録することです。

上の図は、デモ環境に登録された 2 つの cim-server インスタンスを示しています (同じサーバー上にあるため、ポートのみが異なります)。

このようにして、クライアント(この Zookeeper ノードをリッスンしている)は、現在利用可能なサービス情報をリアルタイムで知ることができます。

ログイン

クライアントが cim-forward-route のログイン インターフェイスを要求し (詳細は下記を参照)、ビジネス検証を完了すると (日常的に他の Web サイトにログインする場合と同様に)、前のプロセスで示したように、クライアントはサーバーへの長い接続を開始します。

このとき、クライアントは現在の情報がログイン情報であることを示す特別なメッセージを送信します。メッセージを受信した後、サーバーはクライアントのユーザー ID と現在のチャネル関係を保存する必要があります。

また、ユーザーID とユーザー名などのユーザー情報もキャッシュします。

オフライン

クライアントが切断された場合、キャッシュされた情報もクリアする必要があります。

同時に、関連情報をクリアするために Route インターフェースを呼び出す必要もあります (具体的なインターフェースについては以下を参照してください)。

IMルーティング

アーキテクチャ図からわかるように、ルーティング層は非常に重要なリンクです。クライアントとサーバーを接続するための一連の HTTP サービスを提供します。現在、主なインターフェースは次のとおりです。

①登録インターフェース

各クライアントは使用する前にログインする必要があるため、最初のステップは当然登録です。

ここでの設計は比較的シンプルで、Redis を直接使用してユーザー情報を保存します。ユーザー情報にはIDとユーザー名のみが含まれます。

クエリを容易にするために、Redis の KV は VK を順番に保存するため、ID と userName の両方が一意である必要があります。

②ログインインターフェース

ここでのログインは cim-server のログインとは異なり、ビジネス上の性質を持ちます。

  • ログインが成功した後、それが繰り返しのログインであるかどうかを判断する必要があります (ユーザーは 1 つのクライアントしか実行できません)。
  • ログインに成功したら、Zookeeper からサービス リスト (cim-server) を取得し、特定のアルゴリズムに基づいてサービスを選択してクライアントに返す必要があります。
  • ログインに成功したら、ルーティング情報、つまり現在のユーザーに割り当てられたサービスインスタンスを Redis に保存する必要もあります。

1 人のユーザーだけがログインできるようにするために、Redis の Set を使用してログイン情報を保存します。 userID をキーとして使用すると、重複したログインの書き込みは失敗します。

Java の HashSet と同様に、重複のないデータのみを保存できます。

利用可能なルーティング インスタンスを取得することも比較的簡単です。

  • まず、Zookeeper からすべてのサービス インスタンスを取得して内部キャッシュを作成します。
  • サーバーを選択するためのポーリング (現在はこのアルゴリズムのみが存在し、後で新しいアルゴリズムが追加される予定です)。

もちろん、Zookeeper でサービス インスタンスを取得する前に、cim-server が以前に登録したノードをリッスンする必要があります。

具体的なコードは次のとおりです。

また、アプリケーションの起動後に Zookeeper 内のルーティング ノードを監視し、変更が発生すると内部キャッシュを更新します。

ここでは、Concurrent HashMap に基づく Guava のキャッシュが使用されているため、キャッシュのクリアと追加の原子性が保証されます。

③グループチャットインターフェース

これは実際のメッセージ送信インターフェースです。その結果、1 つのクライアントがメッセージを送信すると、他のすべてのクライアントがそのメッセージを受信できるようになります。

プロセスは、クライアントがサーバーにメッセージを送信することです。サーバーはそれを受信すると、上で紹介した SessionSocketHolder 内のすべてのチャネルをトラバースし、メッセージを送信します。

サーバーは単一のマシンでも構いませんが、現在はクラスター設計になっています。したがって、すべてのクライアントは、以前のポーリング アルゴリズムに従って、異なる cim-server インスタンスに割り当てられます。

したがって、ルーティング層が次のような役割を果たす必要があります。

メッセージを受信した後、ルーティング インターフェイスはまずすべてのクライアントとサービス インスタンス間の関係を走査します。ルーティング関係は次のように Redis に保存されます。

Redis はシングルスレッドであるため、データ量が多い場合は、すべての cim-route:* データと一致するように Keys が使用されると、Redis は他のリクエストを処理できなくなります。

したがって、ここでは Scan コマンドを使用してすべての cim-route:* を走査します。次に、各クライアントが配置されているサーバーの HTTP インターフェースが 1 つずつ呼び出され、メッセージがプッシュされます。

cim-server での実装は次のとおりです。

メッセージを受信した後、cim-server はユーザー ID のチャネルの内部キャッシュを照会し、メッセージを送信します。

④オンラインユーザーインターフェース

これは、現在のオンライン ユーザー情報を照会できる補助インターフェイスです。

実装も非常にシンプルで、以前に「ユーザー ログイン ステータス」を保存したものを照会してリセットするだけです。

⑤プライベートチャットインターフェース

オンラインユーザーを取得することが補助的なインターフェースである理由は、実際にはプライベートチャットを支援するために使用されるためです。

一般的に、プライベート チャットを使用する前に、現在オンラインになっているユーザーを把握する必要があります。そうすれば、誰とプライベート チャットをしたいかがわかります。

次のようなものです:

このシナリオでは、プライベート チャットの前提条件は、オンライン ユーザーのユーザー ID を取得することです。

したがって、メッセージを受信した後、プライベート チャット インターフェイスは受信者がいる cim-server インスタンス情報を照会する必要があり、その後の手順はグループ チャットと同じです。情報を送信するには、受信者が配置されているインスタンスの HTTP インターフェイスを呼び出します。

唯一の違いは、グループ チャットはすべてのオンライン ユーザーに送信されるのに対し、プライベート チャットは 1 人のユーザーにのみ送信されることです。

⑥オフラインインターフェース

クライアントがオフラインになると、Redis に以前保存された一部の情報 (ルーティング情報、ログイン ステータス) を削除する必要があります。

IM クライアント

クライアント内のロジックの一部については、実際に上記で説明しました。

ログイン

最初のステップはログインです。起動時に Route のログイン インターフェイスを呼び出して、cim-server 情報を取得し、接続を作成する必要があります。

ログイン プロセス中に、ルート インターフェイスは繰り返しログインかどうかを判断します。繰り返しログインした場合は、プログラムはそのまま終了します。

次のステップは、ルート インターフェイスによって返される cim-server インスタンス情報 (ip+port) を使用して接続を作成することです。

最初のステップは、クライアントとチャネル間の関係を維持できるように、サーバーにログイン メッセージを送信することです。

カスタムプロトコル

上記のログイン メッセージと実際のメッセージは、カスタム プロトコルで区別できます。

エンコードとデコードには Google Protocol Buffer が使用されるので、まずは元の形式を見てみましょう。

実際、このプロトコルには現在 3 つのフィールドがあります。

  • requestId は userId として理解できます。
  • reqMsg は実際のメッセージです。
  • type は上記のメッセージ カテゴリです。

現在、さまざまなビジネスに対応する 3 つの主要なタイプがあります。

ハートビート

クライアントとサーバー間の接続を維持するために、メッセージが送信されていない場合は、ハートビートを定期的に自動的に送信する必要があります。

現在の戦略は、1 分ごとにハートビート パケットをサーバーに送信することです。

この方法では、サーバーはビジネス メッセージを受信しない場合、1 分ごとに Ping ハートビート パケットを受信します。

組み込みコマンド

クライアントには、使いやすさを考慮していくつかの基本的なコマンドも組み込まれています。

たとえば、:q と入力すると、クライアントが終了し、一部のシステム リソースがシャットダウンされます。

:olu (onlineUser の略) を入力すると、ルートが呼び出され、すべてのオンライン ユーザー インターフェイスが取得されます。

グループチャット

グループチャットの使い方はとても簡単です。コンソールにメッセージを入力して Enter キーを押すだけです。このとき、Route のグループ チャット インターフェイスが呼び出されます。

プライベートチャット

プライベート チャットでも同様ですが、前提条件としてキーワードをトリガーする必要があります。ユーザーIDを使用します。この形式のメッセージ コンテンツは特定のユーザーにメッセージを送信するために使用されるため、通常は、便利に使用する前に :olu コマンドを使用してすべてのオンライン ユーザーを取得する必要があります。

メッセージコールバック

メッセージの保存の必要性など、いくつかのカスタマイズされた要件を満たすため。したがって、クライアントはメッセージを受信した後、実装をカスタマイズできるインターフェースをコールバックします。

したがって、最初に Caller Bean が作成されます。この Bean には CustomMsgHandleListener インターフェースが含まれています。自分で処理する必要がある場合は、このインターフェースを実装するだけで済みます。

インターフェースをカスタマイズする

私はインターフェースを書くのがあまり得意ではないので、インターフェースを書くことができる他の専門家がいると確信しています。したがって、グループ チャット、プライベート チャット、オンライン ユーザー獲得、メッセージ コールバック、およびクライアント内のその他のサービス (および後続のサービス) はすべて、インターフェイスの形式で提供されます。

後からページを統合する場合にも便利です。これらのインターフェースを調整するだけで済みます。具体的な実装について心配する必要はありません。

要約する

Cim は現在最新バージョンに過ぎず、バグが多く、機能も少ないです (テストを行うよう招待されたのは数人のグループ メンバーだけです)。ただし、後で改善される予定です。少なくともこのバージョンは、関連する経験がない友人にいくつかのアイデアをもたらすでしょう。

次のステップ:

<<:  rust-vmm で未来の仮想化アーキテクチャを構築する

>>:  10年ぶりのアリババクラウドの新たなスタートを理解する

推薦する

2018 年のクラウド コンピューティングのトップ 10 の合併と買収はどのようなハリケーン効果をもたらすでしょうか?

[[254606]]合併、買収、再編は市場の活動を判断するための重要な基準です。そこで、本稿では、2...

20 のブランドが 2016 年のブランド コンテンツ マーケティングの 3 つの領域について語ります。

現代社会は消費社会の段階に入っています。これは軽蔑的な言葉ではなく、消費が社会の主な活動となっている...

検索エンジンを使用してウェブサイトのコンテンツを作成する

インターネット上の情報は散在しており、特定のページにおける問題の説明や解決策が不完全です。オリジナル...

詳細説明: Linuxネットワーク仮想化技術

Linux ネットワーク仮想化は、LXC プロジェクトのサブプロジェクトです。 LXC には、ファイ...

競合他社のウェブサイトを分析して自社のウェブサイトを改善する方法

人を鏡として使うと、自分の得失を理解するのに役立ちます。成功しているサイトを真似して学ぶことで、他の...

ウェブサイト分析: ウェブサイトのページブロックの価値を計算する方法

ウェブサイトの設計者が最も気にするのは、「1 インチのスペースも貴重」なホームページの価値を最大化す...

Baidu の 7 つの製品に隠されたプロモーション手法を見てみましょう

世界最大の中国の検索エンジンである百度は、常に高い利用率を誇っています。同社の製品の多くは、ウェブマ...

impactvps-7 USD VPS/4GB RAM/5IP、4つの独立したIPに分割可能

ImpactVPS では、安価な VPS プロモーションを実施しています。通常の VPS とは異なり...

競争的なホットワードを選択する代わりに、すぐに効果が出るコールドワードを選択する方が良いでしょう。

ウェブサイトがキーワードを正しく選択しているかどうかによって、ウェブサイトの成功または失敗が決まりま...

Hawkhost (Eagle Host) - ホスティング/仮想ホスティング/リセラー/セミ仮想ホスティング/Alipay が 45% オフ

Hawkhost は、ロサンゼルス データ センターのホストのプロモーションを開始しました (このプ...

Weiboマーケティング戦略の実践経験の共有(パート2)

前回の2つの記事「Weiboマーケティング戦略の実践経験の共有(パート1)」と「Weiboマーケティ...

APP チャネルの品質を区別する 5 つの重要なポイント。盲目的な試行錯誤のコストを節約します。

チャネル数の増加に伴い、CP の選択肢はますます増えています。しかし、チャネルの品質は多くの CP ...

クラウドコンピューティングが現実世界の環境に与える影響

クラウド コンピューティングは些細なことのように聞こえるかもしれませんが、環境に与える影響は非常に現...

今年のブランドマーケティングトレンド!

2019 年はあっという間に過ぎ去りましたが、マーケティングの世界では、良い意味でも悪い意味でも考え...

Qunarは、UGC獲得競争のために良いレビューを買うためにお金を費やしていると同業他社から批判された。

Qunar.com は昨日、同業他社から公に批判された。マフェンウォ・トラベル・ネットワークのチェン...