【RocketMQ面接の質問(23)】

記事ディレクトリ

RocketMQ 面接の質問 (23 の質問)

ベース

1. メッセージキューを使用する理由は何ですか?

メッセージ キューには主に 3 つの用途があります。例として、電子商取引システムでの注文を考えてみましょう。

  • 分離: メッセージ キューの導入前、注文後、注文サービスは在庫を減らすために在庫サービスを呼び出したり、マーケティング データを追加するためにマーケティング サービスを呼び出したりする必要があります。メッセージ キューの導入後は、注文完了メッセージをキューに投入し、ダウンストリーム サービス自体を呼び出すだけで、注文サービスと他のサービスの分離が完了します。

画像-20230918172901235

  • 非同期:注文決済後、在庫の減算、ポイントの増加、メッセージの送信などを行うためリンクが長くなり、リンクが長くなると応答時間も長くなります。メッセージキューを導入することで、更新订单状态他のすべての処理を非同期で実行できるため、応答時間がすぐに短縮されます。

画像-20230918173002606

  • ピークカット: メッセージキューを 1 つにまとめてピークをカットします。たとえば、フラッシュ セール システムでは、通常はトラフィックが非常に少ないですが、フラッシュ セール活動があると、フラッシュ セール中にトラフィックが猛烈な勢いで殺到します。弊社サーバー、Redis、MySQLはそれぞれ許容範囲が異なるため、全てのトラフィックを順番通りに直接収集すると問題が発生し、ひどい場合は直接ブロックされる可能性があります。

リクエストをキューにスローし、サービスが処理できるトラフィックのみを解放できるため、短期間の大規模なトラフィックに耐えることができます。

画像-20230918181310247

デカップリング、非同期性、およびピーク クリッピングは、メッセージ キューの 3 つの最も重要な機能です。

2. RocketMQ を選ぶ理由は何ですか?

市場にあるいくつかの主要なメッセージ キューの比較は次のとおりです。

ラビットMQ アクティブMQ ロケットMQ カフカ
会社 うさぎ アパッチ アリ アパッチ
言語 アーラン ジャワ ジャワ スカラ&Java
プロトコルのサポート AMPQ OpenWire、STOMP、REST、XMPP、AMQP カスタマイズ カスタム プロトコル。コミュニティは http プロトコルのサポートをカプセル化します。
クライアントがサポートする言語 Erlang、Java、Ruby などを正式にサポートしています。コミュニティでは、ほぼすべての言語をサポートするさまざまな API が見つかりました。 Java、C、C++、Python、PHP、Perl、.netなど Java、C++ (未熟) Java を正式にサポートしており、コミュニティは PHP、Python などのさまざまな API を作成しています。
クリックスループット 3. レベル10,000 4. レベル10,000 1. 10万レベル 2. 10万レベル
メッセージの遅延 マイクロ秒レベル ミリ秒レベル ミリ秒レベル ミリ秒以内
可用性 マスター/スレーブ アーキテクチャに基づく高可用性 マスター/スレーブ アーキテクチャに基づく高可用性 非常に高度な分散アーキテクチャ 非常に高度な分散アーキテクチャ、1 つのデータの複数のコピー
メッセージの信頼性 - データが失われる可能性が低くなります パラメータの最適化と構成後、損失ゼロを達成できます パラメータ設定後、メッセージが失われる可能性があります。
機能サポート Erlang に基づいて開発されているため、同時実行性が非常に高く、パフォーマンスが優れており、レイテンシーが低いです。 MQ分野の機能が非常に充実している MQ は比較的完全な機能と優れた分散スケーラビリティを備えています。 関数は比較的単純で、主に単一の MQ 関数をサポートします。
アドバンテージ Erlang 言語開発、優れたパフォーマンス、低遅延、10,000 レベルのスループット、完全な MQ 機能、非常に優れた管理インターフェイス、活発なコミュニティ、インターネット企業で広く使用されています。 非常に成熟していて強力なので、業界の多数の企業やプロジェクトで使用されています。 インターフェイスはシンプルで使いやすく、アリババ製品が保証され、スループットが大きく、分散拡張が便利で、コミュニティが活発で、大規模なトピックと複雑なビジネスシナリオをサポートし、カスタマイズおよび開発が可能です。ソースコード 超高スループット、ミリ秒レベルの遅延、極めて高い可用性と信頼性、便利な分散拡張
短所 スループットが低く、Erlang 音声開発のカスタマイズは容易ではなく、クラスターの動的拡張は面倒です。 まれにメッセージが失われる可能性が低く、コミュニティの活動が活発ではない場合があります。 インターフェイスは標準の JMS 仕様に従っていないため、一部のシステム移行では大量のコードの変更が必要となり、このテクノロジは廃止される危険があります。 メッセージを
再利用することが可能
応用 全て使用済みです 主にデカップリングと非同期に使用され、大規模なスループット シナリオではあまり使用されません。 大規模なスループットおよび複雑なビジネスで使用されます ビッグデータのリアルタイム計算とログ収集で大規模に使用されており、業界標準となっています。

要約すると:

ミドルウェアを選択する場合は、信頼性、パフォーマンス、機能、操作性、拡張性、コミュニティ活動などの側面を考慮できます。現在、一般的に使用されているミドルウェアがいくつかあります。ActiveMQ は「古い骨董品」であり、市場ではあまり使用されていません。他にもいくつかのミドルウェアがあります。

  • ラビットMQ:

    • 利点: 軽量、高速、導入と使用が簡単、柔軟なルーティング構成が可能
    • 短所: パフォーマンスとスループットが理想的ではなく、二次開発が容易ではない
  • ロケットMQ:

    • 利点: 優れたパフォーマンス、高スループット、安定性と信頼性、活発な中国語コミュニティ
    • デメリット:互換性があまり良くない
  • カフカ:

    • 利点: 強力なパフォーマンスとスループット、優れた互換性
    • 短所: 「波形を保存してから処理する」ため、遅延が比較的大きくなります。

私たちのシステムは、ある程度の同時実行性と比較的高いパフォーマンス要件を備えたユーザー指向の C 側システムであるため、低遅延、比較的高スループット、比較的良好な可用性を備えた RocketMQ を選択しました。

3.RocketMQ の長所と短所は何ですか?

RocketMQ の利点:

  • 単一マシンのスループット: 100,000 レベル
  • 可用性: 非常に高い分散アーキテクチャ
  • メッセージの信頼性: パラメーターの最適化と構成後、メッセージが失われる可能性はゼロです
  • 機能のサポート: MQ は比較的完全な機能を備え、分散されており、優れたスケーラビリティを備えています。
  • 蓄積によるパフォーマンスの低下なしで、10億レベルのメッセージ蓄積をサポートします。
  • ソースコードはJavaとなっており、自社業務と組み合わせた二次開発にも便利です。
  • 金融インターネット分野向けに生まれ、特に電子商取引における注文控除やビジネスのピークカットなど、高い信頼性要件が求められるため、大量のトランザクションが流入すると、バックエンドが時間内に処理できない可能性があります。
  • RoketMQ は、安定性の点でより信頼できる可能性があります。これらのビジネス シナリオは、Alibaba Double 11 中に何度もテストされています。ビジネスに上記の同時実行シナリオがある場合は、RocketMQ を選択することをお勧めします

RocketMQ の欠点:

  • サポートされているクライアント言語はそれほど多くありませんが、現時点では Java と C++ ですが、C++ は未熟です。
  • JMSおよびその他のインターフェイスは MQ コアに実装されていないため、システムによっては移行するために多くのコードを変更する必要があります。

4. メッセージ キューにはどのようなメッセージ モデルがありますか?

メッセージ キューには、キュー モデルパブリッシュ/サブスクライブ モデルの 2 つのモデルがあります。

  • キューモデル

    これは、メッセージ キューの「送信-預け-受信」モデルに対応する、元のメッセージ キュー モデルです。プロデューサはメッセージをキューに送信します。キューは複数のプロデューサからのメッセージを保存でき、キューには複数のコンシューマを含めることもできます。ただし、コンシューマ間には競合関係があるため、各メッセージは 1 つのコンシューマ コンシューマによってのみ送信できます。

画像-20230918200328531

  • パブリッシュ/サブスクライブ モデル

メッセージ データを複数のコンシューマに配布する必要があり、各コンシューマがメッセージの全量を受信する必要がある場合。明らかに、キュー モデルではこの要求を満たすことができません。解決策はパブリッシュ/サブスクライブ モデルです。

パブリッシュ/サブスクライブ モデルでは、メッセージの送信者はパブリッシャーと呼ばれ、メッセージの受信者はサブスクライバーと呼ばれ、サーバー上でメッセージが保存されるコンテナーはトピックと呼ばれます。パブリッシャはトピックにメッセージを送信し、サブスクライバはメッセージを受信する前に「トピックにサブスクライブ」する必要があります。ここでの「サブスクリプション」はアクションであるだけでなく、トピックを消費するときの論理コピーとも考えることができます。各サブスクリプションで、サブスクライバーはトピックのすべてのメッセージを受信できます。

画像-20230918200358126

「キュー モード」との類似点と相違点: プロデューサはパブリッシャ、キューはトピック、コンシューマはサブスクライバです。本質的な違いはありません。唯一の違いは、1 つのメッセージ データを複数回使用できるかどうかです。

5. RocketMQ のメッセージ モデルについてはどうですか?

RocketMQ で使用されるメッセージ モデルは標準のパブリッシュ/サブスクライブ モデルであり、RocketMQ の用語集では、プロデューサ、コンシューマ、トピックはパブリッシュ/サブスクライブ モデルの概念とまったく同じです。

RocketMQ 自体のメッセージは次の部分で構成されます。

画像-20230918200532191

  • メッセージ

メッセージとは、伝達する情報のことです。

メッセージにはトピック (トピック) が必要であり、トピックはレターの宛先アドレスと見なすことができます。

メッセージにはオプションのタグ (タグ) と最後にキーと値のペアを含めることもできます。これを使用してビジネス キーを設定し、ブローカー上でこのメッセージを検索して開発中に問題を見つけることができます。

  • トピック

トピックは、メッセージの第 1 レベルのタイプであるメッセージの分類とみなすことができます。たとえば、電子商取引システムは、トランザクション メッセージ、物流メッセージなどに分類できます。メッセージにはトピックが必要です。

トピックとプロデューサーおよびコンシューマーとの関係は非常に緩やかで、トピックには 0、1、または複数のプロデューサーがメッセージを送信することができ、プロデューサーは同時に異なるトピックにメッセージを送信することもできます。

トピックは、0 人、1 人、または複数のコンシューマーによってサブスクライブすることもできます。

  • 鬼ごっこ

タグは、ユーザーにさらなる柔軟性を提供するために使用される第 2 レベルのメッセージであるサブトピックと考えることができます。タグを使用すると、同じビジネス モジュール内の異なる目的を持つメッセージを、同じトピックで異なるタグで識別できます。たとえば、トランザクション メッセージは、トランザクション作成メッセージ、トランザクション完了メッセージなどに分類できます。メッセージにはタグがない場合があります。

タグは、コードをクリーンで一貫性のあるものに保つのに役立ち、RocketMQが提供するクエリ システムにも役立ちます。

  • グループ

RocketMQ では、サブスクライバーの概念は Consumer Group によって表されます。各コンシューマ グループはトピック内の完全なメッセージを消費し、異なるコンシューマ グループ間の消費の進行状況は相互に影響を受けません。つまり、コンシューマ グループ 1 によって消費されたメッセージは、コンシューマ グループ 2 によっても消費されます。

消費グループには複数のコンシューマが含まれます。同じグループ内のコンシューマは消費をめぐって競合します。各コンシューマは、グループ内のメッセージの一部を消費する責任があります。デフォルトでは、メッセージがコンシューマ Consumer1 によって消費されると、同じグループ内の他のコンシューマはこのメッセージを受信しなくなります。

  • メッセージキュー

メッセージ キュー(メッセージ キュー) では、トピックの下に複数のメッセージ キューを設定でき、トピックには複数のメッセージ キューが含まれます。コンシューマがトピックの下のすべてのメッセージを取得する必要がある場合、すべてのメッセージ キューを横断する必要があります。

RocketMQ には、ConsumerQueue など、他のキューもあります。

  • オフセット

トピック消費プロセス中、メッセージは異なるグループによって複数回消費される必要があるため、消費されたメッセージはすぐには削除されません。これには、RocketMQ が各コンシューマ グループの各キュー上の消費位置 (コンシューマ オフセット) を維持する必要があります。この位置は消費されており、後続のメッセージは消費されていません。メッセージが正常に消費されるたびに、消費位置は 1 つずつ増加します。

Queue無限長の配列とも言えますが、 Offsetは添字です。

RocketMQ のメッセージ モデルでは、これらが重要な概念です。要約するために絵を描きます。

画像-20230918201004095

6. メッセージの消費パターンを理解していますか?

メッセージ消費モードには、クラスタリング(クラスター消費) とブロードキャスト(ブロードキャスト消費) の 2 つがあります。

画像-20230918201137328

デフォルトはクラスター消費です。このモードでは一个消费者组共同消费一个主题的多个队列,一个队列只会被一个消费者消费、コンシューマーが死亡した場合、グループ内の他のコンシューマーが引き継いで消費を続けます。

ブロードキャスト消費メッセージは、消費のためにコンシューマ グループ内のすべてのコンシューマに送信されます。

7.RoctetMQ の基本構造は理解できましたか?

まず、RocketMQ の基本アーキテクチャの図を見てみましょう。

画像-20230918201312128

RocketMQ は、NameServer、Broker、Producer、Consumer の 4 つの部分で構成され、検出、送信、保存、受信に対応しており、高可用性を確保するために、通常、各部分はクラスタにデプロイされます。

8. これら 4 つの部分を紹介していただけますか?

私たちが住んでいる郵便制度にたとえると——

郵便システムの通常の運用は、次の 4 つの役割から切り離すことはできません。1 つは差出人、もう 1 つは受取人、3 つ目は一時保管と送信を担当する郵便局、4 つ目は調整を担当する管理機関です。さまざまな地元の郵便局。RocketMQ に対応するこれら 4 つの役割は、プロデューサー、コンシューマー、ブローカー、およびネームサーバーです。

画像-20230918201430096

ネームサーバー

NameServer はステートレス サーバーであり、その役割は Kafka で使用される Zookeeper に似ていますが、Zookeeper よりも軽量です。
特徴:

  • 各 NameServer ノードは互いに独立しており、相互に情報を交換することはありません。
  • ネームサーバーは、ほぼステートレスになるように設計されています。複数のノードをデプロイすることで、自身を疑似クラスターとして識別します。プロデューサーは、メッセージを送信する前に、ネームサーバーからトピックのルーティング情報、つまりメッセージがどのブローカーに送信されるかを取得します。 Consumer も定期的に NameServer からメッセージを送信し、トピックのルーティング情報を取得するために、Broker は起動時に NameServer に登録し、ハートビート接続を定期的に実行し、維持されているトピックを NameServer に定期的に同期します。

主な機能は 2 つあります。

  • 1. ブローカー ノードとの接続を長時間維持します。
  • 2. トピックのルーティング情報を維持します。
ブローカ

メッセージ ストレージとリレーの役割は、メッセージの保存と転送を担当します。

  • ブローカーは、メッセージのインデックスを保存するために使用されるコンシューマ キューを内部的に維持し、メッセージが実際に保存される場所は CommitLog (ログ ファイル) です。

  • 単一のブローカーは、すべてのネームサーバーとの長い接続とハートビートを維持し、トピック情報をネームサーバーに定期的に同期します。ネームサーバーとの基礎的な通信は、Netty を通じて実装されます。

プロデューサー

メッセージ プロデューサーであるビジネス エンドは、ユーザーによって実装および配布されるメッセージの送信を担当します。

  • プロデューサーはユーザーによって分散された方法でデプロイされ、メッセージは複数のロード バランシング モードを通じてプロデューサーによってブローカークラスターに送信されます。メッセージは低遅延で送信され、高速障害をサポートします。

  • RocketMQ は、同期、非同期、一方向の 3 つのメッセージ送信方法を提供します。

    • 同期送信: 同期送信とは、メッセージ送信者がデータを送信した後、受信者からの応答を受信した後にのみ次のデータ パケットを送信することを意味します。通常、重要な通知メールやマーケティング テキスト メッセージなど、重要な通知メッセージに使用されます。
    • 非同期送信: 非同期送信とは、送信者がデータを送信した後、受信者が応答を返すのを待たずに次のデータ パケットを送信することを意味し、リンクに長時間かかる可能性のあるビジネス シナリオで一般的に使用されます。ユーザーのビデオのアップロードなど、応答時間の影響を受けやすいため、通知後にトランスコーディング サービスが開始されます。
    • 一方向送信: 一方向送信とは、サーバーの応答を待たずにメッセージを送信することのみを担当し、コールバック関数はトリガーされないことを意味します。非常に短時間かかるが、高い信頼性を必要としない特定のシナリオに適しています。ログ収集など。
消費者

メッセージ コンシューマはメッセージの消費を担当し、通常はバックエンド システムが非同期の消費を担当します。

  • Consumerもユーザーによって展開され、PUSH と PULL という 2 つの消費モードをサポートし、クラスター消費ブロードキャスト消費をサポートしリアルタイムのメッセージ サブスクリプション メカニズムを提供します。
  • プル: プル コンシューマーは、メッセージ サーバーから情報をアクティブにプルします。メッセージがバッチでプルされる限り、ユーザー アプリケーションは消費プロセスを開始するため、プルはアクティブ消費と呼ばれます。
  • Push : Push Consumer は、メッセージの取得、消費の進行状況、その他の内部メンテナンス作業をカプセル化し、メッセージがユーザー アプリケーションに到着したときにコールバック インターフェイスを実行したままにします。したがって、プッシュは受動的消費タイプと呼ばれますが、実装の観点から見ると、実際にはメッセージ サーバーからメッセージをプルします。プルとは異なり、プッシュは最初に消費リスナーを登録する必要があり、リスナーが登録されたときにのみメッセージの消費を開始します。引き金になった。

高度な

9. メッセージの可用性、信頼性、損失がないことを保証するにはどうすればよいですか?

どの段階でメッセージが失われる可能性がありますか? 損失は​​、生産段階、保管段階、消費段階の 3 つの段階で発生する可能性があります。

したがって、次の 3 つの段階を検討してください。

画像-20230918202031047

生産

運用段階では、リクエスト確認メカニズムは主に、メッセージの信頼性の高い配信を保証するために使用されます

  • 1. 同期送信する場合は、応答結果と例外の処理に注意してください。応答が OK の場合は、メッセージがブローカーに正常に送信されたことを意味します。応答が失敗するか、他の例外が発生した場合は、再試行する必要があります。
  • 2. 非同期送信する場合はコールバックメソッドで確認し、送信に失敗したり異常な場合は再度送信してください。
  • 3. タイムアウトが発生した場合は、ログ API をクエリすることで、ブローカーへの保存が成功したかどうかを確認することもできます。
ストレージ

ストレージ フェーズでは、信頼性を優先するブローカー パラメーターを構成して、ダウンタイムによるメッセージの損失を避けることができます。簡単に言えば、信頼性が優先されるシナリオでは同期を使用する必要があります。

  • 1. メッセージが CommitLog (ログ ファイル) に保存されている限り、ブローカーがダウンした場合でも、未使用のメッセージを復元して再度使用できます。
  • 2. ブローカーのディスク ブラッシング メカニズム: 同期ディスク ブラッシングと非同期ディスク ブラッシング。どの種類のディスク ブラッシングでも、メッセージはページ キャッシュ (メモリ内) に保存されることが保証されますが、同期ディスク ブラッシングの方が信頼性が高くなります。メッセージの送信後にプロデューサーが待機するデータ。ディスクに保存された後、応答がプロデューサーに返されます。
  • 3. ブローカーは、マスター/スレーブ モードを通じて高可用性を確保します。ブローカーは、マスターとスレーブの同期レプリケーション、マスターとスレーブの非同期レプリケーション モードをサポートします。プロデューサー メッセージはマスターに送信されますが、消費はマスターまたはスレーブから消費できます。同期レプリケーション モードでは、マスターがダウンした場合でも、メッセージは確実にスレーブにバックアップされ、メッセージが失われないことが保証されます。

画像-20230918202927817

消費

消費者の観点から見ると、メッセージが正常に消費されることを確認するにはどうすればよいでしょうか?

  • Consumer がメッセージを確実に消費するための鍵は、確認のタイミングにあり、メッセージ受信後すぐに消費確認を送信するのではなく、すべての消費ビジネス ロジックを実行した後に消費確認を送信します。メッセージ キューは消費位置を維持するため、ロジックの実行は失敗し、確認が行われません。メッセージを再度プルするためにキューに移動しても、前のメッセージのままになります。

10. メッセージの重複の問題にどう対処しますか?

分散メッセージ キューの場合、確実な配信と反復しない配信、いわゆる「1 回限り」の配信を同時に保証することは困難です。RocketMQ はメッセージが失われないように確実な配信を選択していますが、メッセージの重複が発生する可能性があります。

メッセージの重複の問題は主にビジネス側で処理され、主にビジネス冪等性メッセージの重複排除の2 つの方法があります。

ビジネスの冪等性: 1 つ目は、消費ロジックの冪等性、つまり複数の呼び出しと 1 つの呼び出しの効果が同じであることを保証することです。このようにすると、メッセージが何度消費されても、ビジネスに影響を与えることはありません。

メッセージの重複排除: 2 番目のタイプはビジネス エンドであり、重複したメッセージを消費しなくなります。このメソッドでは、各メッセージに一意の番号 (通常は注文番号などのビジネス関連) が付いていることを確認する必要があり、消費記録はデータベースに保存する必要があり、メッセージ確認ステップのアトミック性を保証する必要があります。

具体的な方法は、消費記録テーブルを作成し、このメッセージを取得してデータベースに挿入操作を実行することです。このメッセージに一意の主キー (主キー) または一意の制約を指定します。繰り返し消費が発生した場合でも、主キーの競合が発生し、メッセージは処理されなくなります。

11. メッセージのバックログにどう対処するか?

メッセージのバックログが発生した場合、メッセージのバックログを迅速に消費する方法を見つけ、消費容量の向上を検討する必要があります。一般に、次の 2 つの方法があります。

画像-20230918203127950

  • コンシューマの拡張: 現在のトピックのメッセージ キューの数がコンシューマの数より大きい場合、コンシューマを拡張してさらに多くのコンシューマを追加して、消費容量を増やし、メッセージのバックログをできるだけ早く消費することができます。
  • メッセージの移行 キューの拡張: 現在のトピック内のメッセージ キューの数がコンシューマの数以下である場合、この場合、コンシューマの数を拡張しても意味がないため、メッセージ キューの拡張を検討する必要があります。新しい一時トピックを作成し、その一時トピックにさらにメッセージ キューを設定し、いくつかのコンシューマーを使用して、消費されたデータを一時トピックにスローできます。ビジネス処理の必要がないため、メッセージを転送するだけです。それでも非常に速いです。次に、拡張されたコンシューマを使用して新しいトピックのデータを消費し、消費が完了したら元の状態に戻します。

画像-20230918203319817

12. 連続メッセージを実装するにはどうすればよいですか?

シーケンシャル メッセージは、メッセージの消費順序が製造オーダーと同じであることを意味します。一部のビジネス ロジックでは、注文の生成、支払、配送などの順序が保証される必要があります。このメッセージは順番に処理される必要があります。

順次メッセージは、グローバル順次メッセージと部分順次メッセージに分けられます。

グローバル連続メッセージとは、特定のトピックの下にあるすべてのメッセージが順序を保証する必要があることを意味します。

一部の順次メッセージの場合、メッセージの各グループが順番に消費されることを確認するだけで済みます。たとえば、注文メッセージの場合、同じ注文 ID を持つメッセージが順番に消費できることだけを確認する必要があります。

部分シーケンスメッセージ

部分的な順次メッセージは実装が比較的簡単です。運用側は、同じ ID を持つメッセージを同じメッセージ キューに送信する必要があります。消費プロセス中、同じメッセージ キューから読み取られたメッセージは順番に処理される必要があります。消費者側は同時に処理できません. 部分的な順序付けを実現できるように、連続メッセージを処理します。

画像-20230918203535409

送信者は、MessageQueueSelector クラスを使用して、メッセージがどのメッセージ キューに送信されるかを制御します。

コンシューマ側は、MessageListenerOrderly を使用して、単一のメッセージ キュー内のメッセージが同時に処理される問題を解決します。

グローバルシーケンスメッセージ

RocketMQ は、デフォルトでは順序を保証しません。たとえば、トピックを作成するとき、デフォルトでは 8 つの書き込みキューと 8 つの読み取りキューがあります。このとき、メッセージはどのキューにも書き込まれる可能性があります。データ読み取りプロセス中に、メッセージが書き込まれる可能性があります。複数の Consumer が存在する場合、各 Consumer は並列処理のために複数のスレッドを開始することもあるため、メッセージがどの Consumer によって消費されるか、またメッセージが消費される順序がメッセージが書き込まれた順序と一致するかどうかは不明です。

グローバルな連続メッセージを保証するには、最初にトピックの読み取りおよび書き込みキューの数を 1 に設定し、次にプロデューサー コンシューマーの同時実行設定を 1 に設定する必要があります。簡単に言うと、トピック全体のグローバルメッセージを整然とさせるためには、同時処理を全て排除し、各部分をシングルスレッド処理にするしかありません。の RocketMQ は完全に犠牲になります。

画像-20230918203719183

13. メッセージ フィルタリングを実装するにはどうすればよいですか?

次の 2 つのオプションがあります。

  • 1 つは、Consumer の重複排除ロジックに従って Broker 側でフィルタリングする方法です。この利点は、Consumer 側に無駄なメッセージが送信されないようにすることです。欠点は、Broker の負担が増大し、実装が比較的複雑であることです。
  • もう 1 つは、コンシューマ側でメッセージに設定されたタグに応じた重複排除などのフィルタリングを行う方法で、メリットは実装が簡単であること、デメリットはコンシューマ側に無駄なメッセージが大量に到着し、処理せずに破棄することしかできません。

通常はCosumer側のフィルタリングが使用されますが、スループットを向上させたい場合はBroker側のフィルタリングを使用することもできます。

メッセージをフィルタリングするには 3 つの方法があります。

画像-20230918203833864

  • タグに基づくフィルタリング: これは最も一般的なもので、効率的で使いやすいです。

    DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_EXAMPLE");
    consumer.subscribe("TOPIC", "TAGA || TAGB || TAGC");
    
  • SQL 式フィルタリング: SQL 式フィルタリングがより柔軟になりました

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
// 只有订阅的消息有这个属性a, a >=0 and a <= 3
consumer.subscribe("TopicTest", MessageSelector.bySql("a between 0 and 3");
consumer.registerMessageListener(new MessageListenerConcurrently() {
    
    
   @Override
   public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
    
    
       return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
   }
});
consumer.start();
  • フィルターサーバー方式: 最も柔軟で複雑な方式で、ユーザーがフィルタリング機能をカスタマイズできるようにします。

14. 遅延メッセージを理解できましたか?

タイムアウト後の電子商取引注文の自動キャンセルは、遅延メッセージを使用する典型的な例です。ユーザーが注文を送信した後、遅延メッセージを送信できます。1 時間後に注文のステータスがチェックされます。支払いがまだ行われていない場合、注文はキャンセルされてリリースされます。

RocketMQ は遅延メッセージをサポートしています。メッセージの生成時にメッセージの遅延レベルを設定するだけで済みます。

// 实例化一个生产者来产生延时消息
DefaultMQProducer producer = new DefaultMQProducer("ExampleProducerGroup");
// 启动生产者
producer.start();
int totalMessagesToSend = 100;
for (int i = 0; i < totalMessagesToSend; i++) {
    
    
    Message message = new Message("TestTopic", ("Hello scheduled message " + i).getBytes());
    // 设置延时等级3,这个消息将在10s之后发送(现在只支持固定的几个时间,详看delayTimeLevel)
    message.setDelayTimeLevel(3);
    // 发送消息
    producer.send(message);
}

ただし、RocketMQ で現在サポートされている遅延レベルは制限されています。

private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";

RocketMQ は遅延メッセージをどのように実装しますか?

シンプルな 8 つの単語: 临时存储+ 定时任务.

ブローカーは遅延メッセージを受信すると、まずトピックの対応する期間 (SCHEDULE_TOPIC_XXXX) のメッセージ キューに送信し、次にスケジュールされたタスクを通じてこれらのキューをポーリングします。有効期限が切れると、メッセージはキューに配信されます。ターゲット トピックのメッセージを削除すると、コンシューマはこれらのメッセージを通常どおりに使用できるようになります。

画像-20230918204154153

15. 分散メッセージトランザクションを実装するにはどうすればよいですか? 半分メッセージ?

セミメッセージ: 当面コンシューマが消費できないメッセージを指します。プロデューサはメッセージをブローカーに正常に送信します。ただし、このメッセージは「一時的に配信不能」としてマークされており、プロデューサが送信した後にのみ確認できます。ローカル トランザクションが完了しました。コンシューマはこのメッセージを消費できます。

セミメッセージに依存して、分散メッセージ トランザクションを実装できます。その鍵は二次確認とメッセージ レビューにあります。

画像-20230918204332135

  • 1. プロデューサーがブローカーにハーフメッセージを送信します
  • 2. プロデューサは応答を受信し、メッセージは正常に送信されますが、この時点では、メッセージは「配信不能」としてマークされているハーフメッセージであり、コンシューマは使用できません。
  • 3. プロデューサー側はローカル トランザクションを実行します。
  • 4. 通常の状況では、ローカル トランザクションの実行が完了すると、プロデューサーはブローカーにコミット/ロールバックを送信します。コミットの場合、ブローカーはハーフ メッセージを通常のメッセージとしてマークし、コンシューマーはそれを消費できます。ロールバックの場合、ブローカーはメッセージを破棄します。
  • 5. 異常な状況では、ブローカーは 2 回目の確認を待つことができません。一定時間が経過すると、すべてのハーフ メッセージがクエリされ、その後、プロデューサー側でハーフ メッセージの実行ステータスがクエリされます。
  • 6. プロデューサー側はローカル トランザクションのステータスを問い合わせます
  • 7. トランザクションのステータスに応じて、ブローカーにコミット/ロールバックを送信します。(5、6、7はメッセージレビューです)
  • 8. コンシューマ セグメントはメッセージを消費した後、ローカル トランザクションを実行し、ローカル トランザクションを実行します。

16.デッドレターキューについてご存知ですか?

デッドレターキューは、通常は使用できないメッセージ、つまりデッドレターメッセージを処理するために使用されます。

初めてメッセージの消費に失敗すると、メッセージ キュー RocketMQ は自動的にメッセージを再試行します。最大再試行回数に達した後も消費が失敗する場合は、通常の状況ではコンシューマーがメッセージを正しく消費できないことを意味します。今回は、メッセージ キュー RocketMQ メッセージはすぐには破棄されず、コンシューマに対応する特別なキューに送信されます。この特別なキューはデッド レター キューと呼ばれます。

デッドレターメッセージの特徴:

  • 消費者が通常通りに消費することはできなくなります。
  • 有効期間は通常のメッセージと同じ3日間で、3日を経過すると自動的に削除されます。したがって、デッドレター メッセージは生成後 3 日以内に速やかに処理する必要があります。

デッドレターキューの特徴:

  • デッドレターキューは、単一のコンシューマ インスタンスではなく、グループ ID に対応します。
  • グループ ID がデッドレター メッセージを生成しない場合、メッセージ キュー RocketMQ はそれに対応するデッドレター キューを作成しません。
  • デッドレター キューには、メッセージがどのトピックに属しているかに関係なく、対応するグループ ID によって生成されたすべてのデッドレター メッセージが含まれます。

RocketMQ コンソールは、不正なレター メッセージのクエリ、エクスポート、再送信の機能を提供します。

17. RocketMQ の高可用性を確保するにはどうすればよいですか?

ネームサーバーはステートレスであり、相互に通信しないため、クラスター内にデプロイされている限り、高可用性が保証されます。

画像-20230918205657048

RocketMQ の高可用性は、主に Broker の読み取りと書き込みの高可用性に反映されます。Broker の高可用性は、集群とによって主从実現されます。

画像-20230918205744712

ブローカーは、マスターとスレーブの 2 つのロールで構成できます。マスター ロールのブローカーは読み取りと書き込みをサポートし、スレーブ ロールのブローカーは読み取りのみをサポートします。マスターはメッセージをスレーブに同期します。

つまり、プロデューサーはマスター ロールのブローカーにのみメッセージを書き込むことができ、消費者はマスター ロールとスレーブ ロールのブローカーからのメッセージを読み取ることができます。

Consumer の設定ファイルでは、Master から読み出すか Slave から読み出すかを設定する必要はなく、Master が使用不可またはビジー状態の場合、Consumer の読み出し要求は自動的に Slave に切り替えられます。Consumer の自動切り替えの仕組みにより、Master ロールのマシンに障害が発生した場合でも、Consumer によるメッセージの読み取りに影響を与えることなく、Consumer はスレーブからメッセージを読み取ることができ、高い読み取り可用性を実現します。

送信側で書き込みの高可用性を実現するにはどうすればよいですか? トピックを作成するときは、複数のブローカー グループ (同じブローカー名、異なる BrokerId マシンがブローカー グループを形成する) 上にトピックの複数のメッセージ キューを作成します。これにより、ブローカー グループのマスターが使用できなくなっても、他のグループのマスターは引き続き使用されます。利用可能であり、プロデューサーは引き続きメッセージを送信できます。RocketMQ は現在、スレーブからマスターへの自動変換をサポートしていません。マシンのリソースが不十分で、スレーブをマスターに変換する必要がある場合は、スレーブ色のブローカーを手動で停止し、構成ファイルを作成し、新しい構成ファイルを使用してブローカーを起動します。

原理

18. RocketMQ の全体的なワークフローについて教えてください。

簡単に言うと、RocketMQ は分散メッセージ キュー、つまり消息队列+です分布式系统

メッセージ キューとしては、プロデューサー、ブローカー、およびコンシューマーに対応する - のモデルですが、分散システムとしては、ブローカー、プロデューサー/コンシューマー、およびネームサーバーに対応するサーバー、クライアント、および登録センターが必要です

それでは、その主なワークフローを見てみましょう。RocketMQ は、NameServer 登録センター クラスター、プロデューサー クラスター、コンシューマー クラスター、およびいくつかのブローカー (RocketMQ プロセス) で構成されます。

  1. ブローカーは起動時にすべてのネームサーバーに登録し、長時間の接続を維持し、30 秒ごとにハートビートを送信します。
  2. プロデューサは、メッセージの送信時にネームサーバーからブローカー サーバーのアドレスを取得し、負荷分散アルゴリズムに基づいてメッセージを送信するサーバーを選択します。
  3. Conusmer はメッセージを消費するときに、NameServer からブローカー アドレスも取得し、消費するメッセージをアクティブにプルします。

画像-20230918214743408

19.RocketMQ は登録センターとして Zookeeper を使用しないのはなぜですか?

Kafka が登録センターとして Zookeeper を使用していることは誰もが知っています - もちろん、徐々に Zookeeper を使用し始めていますが、RocketMQ は Zookeeper を使用していません。

CAP 理論は、分散システムでは、一貫性、可用性、およびパーティション耐性を同時に確立することはできないという事実を指します。

  1. 可用性を考慮すると、CAP 理論によれば、同時に満たすことができるのは 2 つのポイントのみであり、Zookeeper は CP を満たすため、Zookeeper はサービスの可用性を保証できません。Zookeeper が選挙を行う場合、選挙時間全体は長すぎます。この期間中、クラスター全体が利用できない状態になります。これは登録センターにとっては絶対に容認できません。サービス ディスカバリとして、可用性を考慮して設計する必要があります。
  2. パフォーマンスを考慮すると、NameServer 自体の実装は非常に軽量であり、クラスターの耐圧性を高めるためにマシンを追加することで水平方向に拡張できます。ただし、Zookeeper の記述はスケーラブルではありません。Zookeeper は領域を分割することによってのみこの問題を解決できます。複数の Zookeeper クラスターの分割この問題を解決するには、まず操作が複雑すぎて、次に CAP の A の設計に違反し、サービス間の切断が発生します。
  3. 永続化メカニズムによって引き起こされる問題: ZooKeeper の ZAB プロトコルは、書き込みリクエストごとに各 ZooKeeper ノードにトランザクション ログを書き込み続け、同時にメモリ データを定期的にディスクにミラーリング (スナップショット) して、データの一貫性と永続性を確保します。ただし、単純なサービス検出シナリオの場合、これは実際には必要ありません。この実装ソリューションは重すぎます。また、保存されるデータ自体も高度にカスタマイズする必要があります。
  4. メッセージ送信は登録センターに弱く依存する必要があり、RocketMQ の設計概念はこれに基づいています。プロデューサーが初めてメッセージを送信するとき、ネームサーバーからブローカー アドレスを取得し、それをローカルにキャッシュします。ネームサーバー クラスター全体の場合、利用できない場合は、短時間でローカルにキャッシュされるため、プロデューサーとコンシューマーには大きな影響はありません。

20.Broker はどのようにデータを保存しますか?

RocketMQ のメイン ストレージ ファイルには、CommitLog ファイル、ConsumeQueue ファイル、および Indexfile ファイルが含まれます。

CommitLog : メッセージ本文とメタデータの格納本体。プロデューサーによって書き込まれたメッセージ本文の内容が格納されます。メッセージの内容は固定長ではありません。単一ファイルのデフォルトのサイズは 1G、ファイル名の長さは 20 桁、左側にゼロが埋め込まれ、残りが開始オフセットです。たとえば、00000000000000000000 は最初のファイルを表し、開始オフセットは 0 です。ファイル サイズは 1G=1073741824; 最初のファイルがいっぱいの場合、2 番目のファイルは 00000000001073741824、開始オフセットは 1073741824 などとなります。メッセージは主にログ ファイルに順次書き込まれ、ファイルがいっぱいになると次のファイルに書き込まれます。

CommitLog ファイルは ${Rocket_Home}/store/commitlog ディレクトリに保存されます。図から、ファイル名のオフセットが明確にわかります。各ファイルのデフォルトは 1G です。いっぱいになると、新しいファイルが自動的に生成されます。

ConsumeQueue : メッセージ消費キュー。これを導入する主な目的は、メッセージ消費のパフォーマンスを向上させることです。RocketMQ はトピックに基づくサブスクリプション モデルであるため、メッセージ消費はトピック用です。コミットログ ファイルをトラバースしてメッセージを取得する場合は、テーマに基づいて考えると、非常に非効率的です。

コンシューマは、ConsumeQueue を使用して、消費するメッセージを見つけることができます。このうち、ConsumeQueue (論理消費キュー) は、消費メッセージのインデックスとして機能し、CommitLog の指定されたトピックの下にあるキュー メッセージの開始物理オフセット offset、メッセージ サイズ size、およびメッセージ Tag の HashCode 値を保存します。

ConsumeQueue ファイルは、Topic に基づく CommitLog インデックス ファイルとみなすことができます。したがって、ConsumeQueue フォルダは次のように構成されます: topic/queue/file の 3 層構造です。具体的なストレージ パスは次のとおりです: $HOME/store/consumequeue/{トピック}/{キュー ID }/{ファイル名}。同様に、ConsumeQueue ファイルも固定長設計を採用しており、各エントリは CommitLog 物理オフセット 8 バイト、メッセージ長 4 バイト、タグ ハッシュコード 8 バイトの合計 20 バイトで、1 ファイルは 30W のエントリで構成されます。各エントリは配列のようにランダムにアクセスでき、各 ConsumeQueue ファイルのサイズは約 5.72M です。

IndexFile : IndexFile (インデックス ファイル) は、キーまたは時間間隔によってメッセージをクエリするメソッドを提供します。インデックス ファイルの保存場所は: {fileName} です。ファイル名 fileName は、作成時のタイムスタンプに基づいて付けられます。単一の IndexFile ファイルの固定サイズは約 400M です。IndexFile は 2000W のインデックスを保存できます。基になるストレージのIndexFile はファイル内に存在するように設計されており、HashMap 構造はシステムに実装されているため、RocketMQ のインデックス ファイルの基礎となる実装はハッシュ インデックスです。

画像-20230918214945104

要約すると、RocketMQ はハイブリッド ストレージ構造を使用します。これは、単一の Broker インスタンスの下にあるすべてのキューが、保存するログ データ ファイル (CommitLog) を共有することを意味します。

RocketMQ のハイブリッド ストレージ構造 (複数のトピックのメッセージ エンティティの内容が CommitLog に保存される) は、プロデューサーとコンシューマーのデータとインデックス部分をそれぞれ分離するストレージ構造を使用します。プロデューサーはメッセージをブローカー側に送信し、ブローカー側は同期を使用します。メッセージを非同期的に永続化し、CommitLog に保存します。

メッセージがディスク ファイル CommitLog に保存されている限り、プロデューサーによって送信されたメッセージは失われません。このため、コンシューマーはこのメッセージを消費する機会を確実に得ることになります。メッセージをプルできない場合は、次のメッセージ プルを待つことができます。同時に、サーバーはロング ポーリング モードもサポートしています。メッセージ プル リクエストがメッセージをプルしない場合、ブローカーは 30 秒間待機することができます。新しいメッセージが到着すると、そのメッセージはコンシューマに直接返されます。

ここで、RocketMQ の具体的なアプローチは、ブローカー側のバックグラウンド サービス スレッドである ReputMessageService を使用してリクエストを継続的に分散し、ConsumeQueue (論理消費キュー) と IndexFile (インデックス ファイル) データを非同期的に構築することです。

画像-20230918215018016

21. RocketMQ がどのようにファイルを読み書きするかについて話してください。

RocketMQ のファイルの読み取りおよび書き込みは、オペレーティング システムのいくつかの効率的なファイル読み取りおよび書き込みメソッド ( 、PageCache顺序读写)を巧みに利用しています零拷贝

  • PageCache、シーケンシャル読み取り

RocketMQ では、ConsumeQueue 論理消費キューに格納されるデータは少なく、順次読み取られます。ページ キャッシュ メカニズムの事前読み取り効果により、メッセージの蓄積がある場合でも、Consume Queue ファイルの読み取りパフォーマンスはメモリの読み取りにほぼ近くなります。パフォーマンスには影響しません。CommitLog メッセージに保存されているログ データ ファイルの場合、メッセージの内容を読み取るときにランダム アクセス読み取りがさらに発生し、パフォーマンスに重大な影響を与えます。スケジューリング アルゴリズムを「デッドライン」に設定するなど、適切なシステム IO スケジューリング アルゴリズムを選択すると (現時点でブロック ストレージが SSD を使用している場合)、ランダム読み取りのパフォーマンスも向上します。

ページ キャッシュ (PageCache) は、ファイルの読み取りと書き込みを高速化するために使用される OS のファイル キャッシュです。一般に、プログラムによるファイルの連続読み取りおよび書き込みの速度は、メモリの読み取りおよび書き込みの速度にほぼ近くなります。主な理由は、OS が PageCache メカニズムを使用して読み取りおよび書き込みアクセス操作のパフォーマンスを最適化し、メモリの一部を PageCache として使用します。データの書き込みの場合、OS はまずデータをキャッシュに書き込み、次に pdflush カーネル スレッドがキャッシュ内のデータを非同期方法で物理ディスクにフラッシュします。データ読み取りの場合、ファイル読み取り時にPageCacheミスが発生した場合、OSは読み取ったファイルに物理ディスクからアクセスしながら、隣接する他のブロックのデータファイルを順次先読みします。

  • ゼロコピー

さらに、RocketMQ は主に MappedByteBuffer を通じてファイルの読み取りと書き込みを行います。その中で、NIO の FileChannel モデルは、ディスク上の物理ファイルをユーザー モード メモリ アドレスに直接マップするために使用されます (この Mmap メソッドは、従来の IO を削減し、ディスク ファイル データをオペレーティング システムのカーネル アドレス空間のバッファに保存し、パフォーマンスを向上させます)ユーザー アプリケーションのアドレス空間内のバッファ間でのコピーのオーバーヘッドを軽減し、ファイル操作をメモリ アドレスでの直接操作に変換し、ファイルの読み取りと書き込みの効率を大幅に向上させます (メモリ マッピング メカニズムを使用する必要があるため)。 RocketMQ のファイル ストレージは、ファイル全体を一度にメモリにマッピングすることを容易にするために固定長構造を使用します。

ゼロコピーとは何ですか?

オペレーティング システムでは、従来の方法を使用すると、データを数回コピーし、ユーザー モードとカーネル モードの切り替えを行う必要があります。

画像-20230918215058881

  1. ディスクからカーネル モード メモリにデータをコピーします。
  2. カーネル モード メモリからユーザー モード メモリにコピーします。
  3. 次に、ユーザー モード メモリからネットワーク ドライバのカーネル モード メモリにコピーします。
  4. 最後に、ネットワーク ドライバーのカーネル モード メモリからネットワーク カードにコピーされて送信されます。

したがって、ゼロ コピーを使用すると、ユーザー モードとカーネル モード間のコンテキスト スイッチとメモリ コピーの数を減らし、I/O パフォーマンスを向上させることができます。ゼロ コピーのより一般的な実装はmmapであり、このメカニズムは MappedByteBuffer を通じて Java に実装されます。

画像-20230918215120209

22. メッセージのフラッシュはどのように実装されますか?

RocketMQ は、同期ディスク ブラッシングと非同期ディスク ブラッシングという 2 つのディスク ブラッシング戦略を提供します。

  • 同期フラッシュ: メッセージがブローカーのメモリに到達した後、成功したとみなされるためにはメッセージを commitLog ログ ファイルにフラッシュする必要があります。その後、データが正常に送信されたことがプロデューサーに返されます。
  • 非同期フラッシュ: 非同期フラッシュとは、メッセージがブローカー メモリに到達した後、データが正常に送信されたことをプロデューサーに返し、スレッドが起動されてデータを CommitLog ログ ファイルに保存することを意味します。

ブローカーはメッセージにアクセスするときにメモリ (メモリ マップされたファイル) を直接操作します。これによりシステムのスループットが向上しますが、マシンの電源がオフになったときのデータ損失は避けられないため、ディスクに永続化する必要があります。

ディスク ブラッシングの最終実装では、NIOの MappedByteBuffer.force() を使用して、マッピング領域のデータをディスクに書き込みます。同期ディスク ブラッシングの場合、ブローカーはメッセージをCommitLogマッピング領域に書き込んだ後、待機します。書き込みを完了します。

非同期動作に関しては、対応するスレッドを起こすだけであり、実行のタイミングは保証されませんが、その処理は図のとおりです。

画像-20230918215157713

22. RocketMQ の負荷分散がどのように実装されているか教えてください。

RocketMQ における負荷分散はクライアント側で完結しますが、具体的には、Producer 側がメッセージを送信するときの負荷分散と Consumer 側がメッセージをサブスクライブするときの負荷分散に分けられます。

プロデューサーの負荷分散

プロデューサーがメッセージを送信するとき、最初にトピックに基づいて指定された TopicPublishInfo を見つけます。TopicPublishInfo ルーティング情報を取得した後、RocketMQ クライアントはデフォルトで selectOneMessageQueue() メソッドで TopicPublishInfo の messageQueueList からキュー (MessageQueue) を選択します。メッセージを送信します。ここには sendLatencyFaultEnable スイッチ変数があり、これをオンにすると、使用できないブローカー エージェントがランダムな増分モジュロに基づいて除外されます。

画像-20230918215220144

いわゆる「latencyFaultTolerance」とは、以前の障害に対する一定期間のバックオフを指します。たとえば、最後のリクエストのレイテンシが 550Lms を超えた場合は 3000Lms がバックオフされ、1000L を超えた場合は 60000L がバックオフされ、キューが閉じられている場合は、ランダムな増分係数を使用してメッセージを送信するキュー (MessageQueue) が選択されます。 latencyFaultTolerance メカニズムは、メッセージ送信の高可用性を実現するためのコア キーです。

消費者の負荷分散

RocketMQ では、コンシューマ側の 2 つの消費モード (プッシュ/プル) は、メッセージを取得するためのプル モードに基づいています。プッシュ モードは、プル モードの単なるカプセル化です。その本質は、メッセージ プル スレッドがメッセージをプルすることです。メッセージのバッチを取得し、メッセージ消費スレッド プールに送信した後、サーバーからのメッセージのプルを「ノンストップ」で試行し続けます。メッセージが取得されない場合は、しばらく遅延してから再度取得されます。どちらのプルベースの消費方法 (プッシュ/プル) においても、コンシューマー側は、ブローカー側のどのメッセージ キューからメッセージを取得するかを知る必要があります。したがって、Consumer 側で負荷分散を行う必要があります。つまり、Broker 側の複数の MessageQueue を、同じ ConsumerGroup 内の Consumer が消費するように割り当てます。

  1. Consumer側でのハートビートパケット送信

Consumer が開始されると、スケジュールされたタスク (メッセージ消費グループの名前、サブスクリプション関係のコレクション、メッセージ通信モード、値などの情報が含まれます) を通じて、RocketMQ クラスター内のすべての Broker インスタンスにハートビート パケットを継続的に送信します。クライアント ID) 。Consumer のハートビート メッセージを受信した後、Broker はそれを ConsumerManager のローカル キャッシュ変数 - ConsumerTable に維持し、同時にコンシューマ側での後続のロードに備えて、カプセル化されたクライアント ネットワーク チャネル情報をローカル キャッシュ変数 - channelInfoTable に保存します。 . Balance は、信頼できるメタデータ情報を提供します。

  1. Consumer 側でロード バランシングを実装するためのコア クラス — RebalanceImpl

Consumer インスタンスの起動プロセスでは、MQClientInstance インスタンスの起動により、負荷分散サービス スレッド RebalanceService (20 秒ごとに実行) の起動が完了します。

ソースコードを見ると、RebalanceService スレッドの run() メソッドが最終的に RebalanceImpl クラスの rebalanceByTopic() メソッドを呼び出していることがわかり、このメソッドがコンシューマ側の負荷分散を実現するための中核となります。

rebalanceByTopic() メソッドは、コンシューマの通信タイプが「ブロードキャスト モード」か「クラスタ モード」かに応じて異なる論理処理を実行します。ここでは主にクラスターモードでの主な処理手順を見ていきます。

(1) rebalanceImpl インスタンスのローカル キャッシュ変数から Topic トピックの下のメッセージ消費キュー セット (mqSet) を取得します (topicSubscribeInfoTable)。

(2) パラメータとしてトピックおよび ConsumerGroup に基づいて mQClientFactory.findConsumerIdList() メソッドを呼び出し、ブローカーに通信要求を送信して、コンシューマ グループの下のコンシューマ ID のリストを取得します。

(3) まず、トピックの下でメッセージ消費キューとコンシューマ ID をソートし、次にメッセージ キュー割り当て戦略アルゴリズム (デフォルトはメッセージ キューの平均割り当てアルゴリズム) を使用して、プルされるメッセージ キューを計算します。ここでの平均割り当てアルゴリズムはページング アルゴリズムに似ており、すべての MessageQueue をレコードのように並べ替え、すべてのコンシューマ コンシューマをページ数のように並べ替え、各ページに含める必要がある各ページの平均サイズとレコードを見つけます。範囲全体を走査して、現在のコンシューマ側に割り当てる必要がある MessageQueue を計算します。

画像-20230918215304192

(4) 次に、updateProcessQueueTableInRebalance() メソッドを呼び出します。具体的な方法としては、まず、割り当てられたメッセージ キュー セット (mqSet) と processQueueTable の間でフィルタリング比較を実行します。

画像-20230918215322343

  • 上図の processQueueTable でマークされた赤色の部分は、割り当てられたメッセージ キュー セット mqSet に含まれていないことを示します。これらのキューの Dropped 属性を true に設定し、これらのキューを processQueueTable キャッシュ変数から削除できるかどうかを確認します。ここでは、removeUnnecessaryMessageQueue() メソッドが具体的に実行され、つまり 1 秒ごとに、消費電流のロックが解除されているかどうかを確認します。処理キューを取得でき、取得した場合は true を返します。1秒待っても消費電流処理キューのロックが取得できない場合はfalseを返します。true が返された場合、対応するエントリが processQueueTable キャッシュ変数から削除されます。
  • 上の図の processQueueTable の緑色の部分は、割り当てられたメッセージ キュー セット mqSet との交差部分を表します。ProcessQueue の有効期限が切れているかどうかを確認します。プル モードでは心配する必要はありません。プッシュ モードの場合は、Dropped 属性を true に設定し、removeUnnecessaryMessageQueue() メソッドを呼び出して、上記のようにエントリの削除を試みます。
  • 最後に、フィルター処理されたメッセージ キュー セット (mqSet) 内の各 MessageQueue に対して ProcessQueue オブジェクトが作成され、RebalanceImpl の processQueueTable キューに格納されます (ここで、RebalanceImpl インスタンスの computePullFromWhere(MessageQueue mq) メソッドが呼び出され、 MessageQueue オブジェクトの値のオフセットを取得し、次に作成する pullRequest オブジェクトの属性に埋め込みます)、プル リクエストのオブジェクト-pullRequest を作成し、プル リスト-pullRequestList に追加します。最後に、dispatchPullRequest() メソッドを実行して、プル メッセージのリクエスト オブジェクト PullRequest を順番に呼び出し、PullMessageService サービス スレッドのブロッキング キュー pullRequestQueue に入り、サービス スレッドが取り出された後、ブローカーへのプル メッセージ リクエストを開始します。このうち比較に注目すると、RebalancePushImpl と RebalancePullImpl の 2 つの実装クラスのdispatchPullRequest() メソッドが異なり、RebalancePullImpl クラスのメソッドは空です。

同じコンシューマ・グループ内の異なるコンシューマ間でのメッセージ消費キューのロード・バランシングの中心的な設計コンセプトは、メッセージ消費キューは同じコンシューマ・グループ内の 1 つのコンシューマによってのみ同時に消費できること、およびメッセージ消費キューは複数のコンシューマを消費できるということです。同時にメッセージを送信するメッセージキュー。

23.RocketMQ メッセージのロングポーリングについて理解していますか?

いわゆるロング ポーリングとは、Consumer がメッセージをプルすることを意味します。対応する Queue にデータがない場合、Broker はすぐには戻りません。代わりに、PullReuqest を保持し、キューにメッセージが入るまで待機するか、ポーリング ブロック時間が長くなります。このキューのすべての PullRequest を処理します。

画像-20230918215353728

  • PullMessageProcessor#processRequest

    //如果没有拉到数据
    case ResponseCode.PULL_NOT_FOUND:
    // broker 和 consumer 都允许 suspend,默认开启
    if (brokerAllowSuspend && hasSuspendFlag) {
          
          
        long pollingTimeMills = suspendTimeoutMillisLong;
        if (!this.brokerController.getBrokerConfig().isLongPollingEnable()) {
          
          
            pollingTimeMills = this.brokerController.getBrokerConfig().getShortPollingTimeMills();
        }
    
        String topic = requestHeader.getTopic();
        long offset = requestHeader.getQueueOffset();
        int queueId = requestHeader.getQueueId();
        //封装一个PullRequest
        PullRequest pullRequest = new PullRequest(request, channel, pollingTimeMills,
                                                  this.brokerController.getMessageStore().now(), offset, subscriptionData, messageFilter);
        //把PullRequest挂起来
        this.brokerController.getPullRequestHoldService().suspendPullRequest(topic, queueId, pullRequest);
        response = null;
        break;
    }
    

保留中のリクエストの場合、サービス スレッドはキューにデータがあるかどうかを常にチェックし、データが存在しない場合はタイムアウトになります。

  • PullRequestHoldService#run()
@Override
public void run() {
    
    
    log.info("{} service started", this.getServiceName());
    while (!this.isStopped()) {
    
    
        try {
    
    
            if (this.brokerController.getBrokerConfig().isLongPollingEnable()) {
    
    
                this.waitForRunning(5 * 1000);
            } else {
    
    
                this.waitForRunning(this.brokerController.getBrokerConfig().getShortPollingTimeMills());
            }

            long beginLockTimestamp = this.systemClock.now();
            //检查hold住的请求
            this.checkHoldRequest();
            long costTime = this.systemClock.now() - beginLockTimestamp;
            if (costTime > 5 * 1000) {
    
    
                log.info("[NOTIFYME] check hold request cost {} ms.", costTime);
            }
        } catch (Throwable e) {
    
    
            log.warn(this.getServiceName() + " service has exception. ", e);
        }
    }

    log.info("{} service end", this.getServiceName());
}

データ出典:クソ野郎の逆襲: RocketMQ Twenty-three の質問 (qq.com)

おすすめ

転載: blog.csdn.net/weixin_45483322/article/details/132998780