記事ディレクトリ
RocketMQ ソース コードを学ぶ必要があるのはなぜですか?
- エレガントで効率的なコードを作成します。RocketMQ は、Alibaba のダブル イレブン トランザクション向けのコア リンク製品として、数千万の同時実行と数兆のデータ ピークをサポートします。ソース コードを読むと、効率的で洗練されたコードを書く経験を積むことができます。
- 思考とコンセプトに焦点を当てて、マイクロアーキテクチャ設計能力を向上させます。トップレベルの Apache プロジェクトとして、Apache RocketMQ のアーキテクチャ設計は学ぶ価値があります。
- 仕事や勉強でさまざまな困難で複雑な病気を解決します。RocketMQ の使用中に消費の滞りや遅延などの問題が発生した場合は、ソース コードを読むことで問題を見つけて解決できます。
- BATJ の一流インターネット企業の面接で優れた自分をアピールしましょう。大手企業、特に Alibaba ベースの企業との面接では、RocketMQ ソース コードに関する体系的な知識があると間違いなく大きなプラスとなります。
RocketMQ ソース コードの技術的なハイライト
- 読み書きロック
- アトミック操作クラス
- ファイルストレージの設計
- ゼロコピー:MMAP
- スレッドプール
- 同時ハッシュマップ
- コピーオンライトコンテナ
- 負荷分散戦略
- 障害遅延メカニズム
- オフヒープメモリ
RocketMQ モジュールの構造
RocketMQ の全体的なモジュールは次のとおりです。
- rocketmq-namesrv : ネーミングサービス。更新およびルート検出ブローカー サービス。メッセージ プロデューサとメッセージ コンシューマにトピックに関するルーティング情報を提供します。NameServer は、基本的なルーティング情報の保存に加えて、ルーティングの登録、ルーティングの削除、その他の機能を含むブローカー ノードを管理できる必要があります。
- rocketmq-broker : mq のコア。プロデューサとコンシューマからリクエストを受信し、ストア層サービスを呼び出してメッセージを処理できます。HA サービスの基本ユニットは、同期二重書き込み、非同期二重書き込み、およびその他のモードをサポートします。
- rocketmq-store : インデックス サービスと高可用性 HA サービスの実装を含むストレージ層の実装。
- rocketmq-remoting : netty に基づく基礎となる通信実装。サービス間のすべての対話はこのモジュールに基づいています。
- rocketmq-common : 一部の構成ファイルや定数など、モジュール間で共通の機能クラス。
- rocketmq-client : Java バージョンの MQ クライアント実装
- rocketmq-filter : メッセージ フィルタリング サービス。ブローカーとコンシューマの間にフィルタ エージェントを追加するのと同等です。
- rocketmq-srvutil : ServerUtil、コマンド ラインを解析するためのツール クラス。
- rocketmq-tools : メッセージクエリなどの機能を提供する mq クラスター管理ツール
RocketMQ には多くのソース コードがあります。RocketMQ のすべてのソース コードを読む必要はありません。コアと主要なソース コードを解釈します。RocketMQ のコア プロセスは次のとおりです。
- 起動プロセス
RocketMQ サーバーは、NameServer と Broker の 2 つの部分で構成されます。NameServer はサービスの登録センターです。ブローカーは、そのアドレスを NameServer に登録します。プロデューサーとコンシューマが起動すると、最初に NameServer からブローカーのアドレスを取得し、次に、ブローカーはメッセージを送受信します。 - メッセージ生成プロセス
プロデューサーは、RocketMQ クラスター内のブローカーの特定のキューにメッセージを書き込みます。 - メッセージ消費プロセス
Comsumer は、RocketMQ クラスターから対応するメッセージをプルし、消費を確認します。
NameServerのソースコード分析
NameServer 全体のプロセス
NameServer は RocketMQ 全体の「頭脳」であり、RocketMQ のサービス登録センターであるため、RocketMQ は最初に NameServer を起動してから、Rocket のブローカーを起動する必要があります。
- NameServer が開始されます。
リッスンを開始し、Broker、Producer、および Comsumer の接続を待ちます。ブローカーは起動時にすべてのネームサーバーに登録します。プロデューサは、メッセージを送信する前にネームサーバーからブローカー サーバーのアドレス リストを取得し、ロード バランシング アルゴリズムに基づいてメッセージを送信するサーバーをリストから選択します。コンシューマーは、トピックのメッセージをサブスクライブする前に、NamerServer からブローカー サーバーのアドレス リスト (クラスターの場合もあります) を取得しますが、コンシューマーは、ブローカーからのメッセージをサブスクライブすることを選択します。サブスクリプション ルールは、ブローカーの構成によって決まります。 - ルーティング登録
ブローカーが開始されると、ルーティングおよびハートビート情報がすべてのネームサーバーに送信されます。 - Route Elimination
NameServer は、各 Broker サービスとの接続を長時間維持し、10 秒ごとに Broker が生存しているかどうかをチェックし、Broker がダウンしていることを検出すると、その Broker をルーティング レジストリから削除します。このようにして、RocketMQ の高可用性を実現できます。
ネームサーバーの起動プロセス
NameServerは別途起動します。エントリ クラス: NamesrvController。フローチャートは次のとおりです。
KV 構成をロードする
NamesrvController クラスの createNamesrvController() のコア解釈
ソースコードに p パラメータがあることが分かりました。起動パラメータに -p を直接入力すると、ネームサーバーのすべてのパラメータ情報が出力されます (ただし、ネームサーバーは自動的に終了します)。この -p がパラメータであることを示します。テストパラメータ。
通常の起動中に、すべてのパラメータが起動ログに記録されます。
ルーティングとハートビート情報を受信するためのNRS通信を構築します
スケジュールされたタスクによりタイムアウトが排除されます。
コア コントローラーはスケジュールされたタスクを開始します。つまり、10 秒ごとにブローカーをスキャンし、非アクティブなブローカーを削除します。
ブローカーは、30 秒ごとにハートビート パケットをネームサーバーに送信します。ハートビート パケットには、BrokerId、ブローカー アドレス、ブローカー名、ブローカーが属するクラスターの名前、およびブローカーに関連付けられた FilterServer のリストが含まれます。
しかし、ブローカーがダウンし、ネームサーバーがハートビート パケットを受信できない場合、ネームサーバーはどのようにしてこれらの障害のあるブローカーを排除するのでしょうか? NameServer は、brokerLiveTable ステータス テーブルを 10 秒ごとにスキャンします。BrokerLive のlastUpdateTimestampのタイムスタンプが現在時刻から 120 秒を超えている場合、ブローカーは無効とみなされ、ブローカーが削除され、ブローカーとの接続が閉じられ、topicQueueTable、 BrokerAddrTable、brokerLiveTable、filterServerTable は同時に更新されます。
ただし、この設計には問題があり、ネームサーバーが利用可能であると認識しているブローカーが実際にダウンしている場合、ネームサーバーから読み取られたルートに利用できないホストが含まれ、異常なメッセージの生成/消費が発生します。この問題は、本番側と消費者側の障害回避戦略と再試行メカニズムによって解決できます。この設計は、RocketMQ の設計哲学に沿ったもので、全体的な設計はシンプルさとパフォーマンスを追求すると同時に、NameServer はステートレスになるように設計されており、複数のサーバーを自由に配置でき、コードも非常にシンプルかつ軽量です。
RocketMQ には、ルーティング情報を削除するための 2 つのトリガー ポイントがあります。
- NameServer は定期的に BrokerLiveTable をスキャンして、最後のハートビート パケットと現在のシステムの間の時間差を検出します。時間が 120 秒を超える場合は、ブローカーを削除する必要があります。
- ブローカーが正常にシャットダウンされると、unregisterBroker コマンドが実行されますが、ルーティングの削除方法はどちらも同じであり、該当するルーティング テーブルからブローカーに関する情報を削除します。
コンシューマが起動した後の最初のステップは、ネームサーバーからトピック関連情報を取得することです。
ネームサーバー設計のハイライト
読み書きロック
RouteInfoManager クラスには読み取り/書き込みロック設計があります。
メッセージが送信されると、クライアントはネームサーバーからルーティング情報を取得し、ブローカーはネームサーバーのルーティング情報を定期的に更新するため、ルーティング テーブルでは次の操作が非常に頻繁に行われます。
- プロデューサーがメッセージを送信するとき、トピックを頻繁に取得してトピック テーブルを読み取る必要があります。
- ブローカーはルーティング テーブルを定期的に (30 秒) 更新し、トピック テーブルに書き込みます。
特にプロデューサがメッセージを送信する場合、頻繁な読み取りと書き込みによって同時実行性が向上するため、ここでは読み取り/書き込みロック メカニズムが使用されます (読み取りが多く書き込みが少ないシナリオ用)。
Synchronized ロックと ReentrantLock は基本的に排他ロックです。排他ロックでは 1 つのスレッドのみが同時にアクセスできますが、読み取り/書き込みロックでは複数の読み取りスレッドが同時にアクセスできます。ただし、書き込みスレッドがアクセスすると、すべての読み取りスレッドと他のライターのスレッドはブロックされています。リードライトロックは、リードロックとライトロックのペアのロックを保持しており、リードロックとライトロックを分離することで、一般的な排他ロックに比べて同時実行性が大幅に向上します。
ストレージはメモリに基づいています
NameServer には次の情報が保存されます。
topicQueueTable : トピック メッセージ キューのルーティング情報。メッセージ送信時にルーティング テーブルに基づいて負荷分散が実行されます。
BrokerAddrTable : BrokerName、クラスター名、アクティブおよびバックアップ ブローカーのアドレスなどのブローカーの基本情報
clusterAddrTable : ブローカー クラスター情報。クラスター内のすべてのブローカー名を格納します。
BrokerLiveTable : ブローカーのステータス情報。NameServer はハートビート パケットを受信するたびにこの情報を置き換えます。
filterServerTable : クラス モード メッセージ フィルタリングに使用される、ブローカー上の FilterServer リスト。
NameServer の実装はメモリに基づいています。NameServer はルーティング情報を永続化しません。永続化という重要なタスクは Broker に任されています。この設計により、NameServer の処理能力が向上します。
ネームサーバーステートレス
- これらは、NameServer クラスター内では相互に通信しません。
- マスター/スレーブ アーキテクチャでは、ブローカーはルーティングとハートビート情報をすべてのネームサーバーに登録します。
- プロデューサ/コンシューマは、NameServer クラスタの 1 つとの長時間接続を同時に確立します。
RocketMQ クラスターが 2 つのコンピューター ルームに展開されていると仮定します。各コンピューター ルームにはいくつかのネームサーバー、ブローカー、およびクライアント ノードがあります。2 つのコンピューター ルーム間のリンクが中断された場合、すべてのネームサーバーはサービスを提供でき、クライアントはローカル ノードにのみ存在できます。コンピュータ ルーム。NameServer でこのコンピュータ ルームのブローカーを見つけます。
RocetMQ クラスターでは、NameServer は相互に通信する必要がないため、ネットワーク パーティションは NameServer 自体の可用性に影響を与えません。NameServer がブローカーへの接続が中断されたことを検出すると、NameServer はブローカーが通信できると判断します。サービスは提供されなくなり、クライアントが利用できないブローカーに接続するのを防ぐために、このブローカーのネームサーバーはルーティング情報から直ちに削除されます。
ネットワークが分割されると、ネームサーバーはピア コンピューター ルームのブローカーのハートビートを受信できなくなり、この時点では、各ネームサーバーはローカル コンピューター ルームのブローカー情報のみを保持します。