RocketMQ:プロデューサーのソースコード分析

以前にプロデューサーのソースコードを見て作成したメモをアップロードします。

テキストは次のとおりです。

 

RocketMQバージョン:4.5.1

クライアントは、rocketmq / clientディレクトリにある別のモジュールです。

1.提案:単体テストからのProduceAPIの使用を見てください

一般的な単体テストでは、各ユースケースはテストコードまたは小さなプロセスの一部です。

一部の比較的完全なオープンソースソフトウェアの場合、単体テストのカバレッジは非常に高く、関心のあるプロセスに対応するテストケースを簡単に見つけることができます。ソースコード分析は、これらのテストケースから開始し、メソッド呼び出しリンクを段階的にたどり、実装プロセスを明確にすることができます。
まず、RocketMQクライアントの単体テストを分析して、プロデューサーが提供するAPIを確認し、さらに重要なことに、これらのAPIの使用方法を理解しましょう。
プロデューサーのすべてのテストケースは、同じテストクラスである「org.apache.rocketmq.client.producer。DefaultMQProducerTest」。このテストクラス内のすべてのユニットテストメソッドを見ると、あなたがおおよそプロデューサーの主な機能を理解することができます。

このテストクラスの主なテストメソッドは次のとおりです。

 

初期化

終了する

testSendMessage_ZeroMessage testSendMessage_NoNameSrv testSendMessage_NoRoute testSendMessageSync_Success testSendMessageSync_WithBodyCompressed testSendMessageAsync_Success testSendMessageAsync testSendMessageAsync_BodyCompressed testSendMessageSync_SuccessWithHook

その中で、initterminateは、テストの初期化を開始し、テストを破棄するときに実行する必要のあるコードです。testSendMessageで始まる他のメソッドは、さまざまな状況やシナリオでメッセージを送信するためのテストケースです。これらのユースケースでは、テストの機能を大まかに見ることができます。
たとえば、testSendMessageSynctestSendMessageAsyncは同期送信と非同期送信のテストケースでありtestSendMessageSync_WithBodyCompressedは圧縮メッセージ送信のテストケースです。
RocketMQのようなオープンソースプロジェクトは、初期段階でテストケースの作成に多くの時間を費やしているため、時間の無駄に思えます。実際、後の共同デバッグテスト、統合テスト、およびその後の問題解決のために多くの時間を節約できます。オンラインにすると、回線を効果的に減らすことができます。失敗の可能性は、一般的に非常に費用対効果が高くなります。毎日の開発プロセスでより多くのテストケースを作成し、50%を超える単体テストカバレッジを達成するようにすることを強くお勧めします。
RockectMQのプロデューサーエントリークラスは「org.apache.rocketmq.client.producerあるDefaultMQProducer。」コードとクラス間の継承関係を簡単に見て、次のように、私はプロデューサーに関連するいくつかのコアクラスとインタフェースを整理しています:

RocketMQは、デザインパターンを使用します。ファサードパターン(ファサードパターン)は、外観パターンとも呼ばれます
ファサードモードの主な機能は、システムにアクセスしてシステムの内部の複雑さを隠すことができるインターフェイスをクライアントに提供することです。
インターフェイスMQProducerは、このモードのファサードです。クライアントは、このインターフェイスを使用している限り、プロデューサーにアクセスしてメッセージ送信の関連機能を実現できます。使用レベルから、他の複雑な実装クラスを処理する必要はありません。
DefaultMQProducerクラスはインターフェイスMQProducerを実装し、その中のほとんどのメソッド実装にはビジネスロジックがありませんが、ファサードの一部として理解できる他の実装クラスへのメソッド呼び出しのみをカプセル化します。Producerのビジネスロジックのほとんどは、後で焦点を当てるDefaultMQProducerImplクラスに実装されています。
実装が多くの内部クラスに散在している場合があり、インターフェイスを使用して外部サービスを提供するのは便利ではありません。RocketMQのアプローチを模倣し、ファサードモードを使用して、内部実装を非表示にし、外部サービスを提供できます。
インターフェイスMQAdminは、メッセージ送信プロセスで使用されるいくつかのメタデータ管理メソッドを定義します。

第二に、起動プロセス

単体テストのコードから、init()terminate()2つのテストメソッドで、プロデューサーのstartメソッドshutdownメソッドがそれぞれ実行されていることがわかります。これは、RocketMQではプロデューサーがステートフルサービスであることを示しています。これは、メッセージの前にプロデューサーを開始する必要があります。この起動プロセスは、実際にはメッセージ送信の準備です。したがって、メッセージ送信プロセスを分析する前に、プロデューサーで維持されている状態と、起動プロセス中にプロデューサーが実行する初期化作業を明確にする必要があります。この基盤がなければ、メッセージ送信の実装プロセスを分析できません。
まず、テストケースのinit()メソッドから始めます。
 

@Before
public void init() throws Exception {
	String producerGroupTemp = producerGroupPrefix + System.currentTimeMillis();
	producer = new DefaultMQProducer(producerGroupTemp);
	producer.setNamesrvAddr("127.0.0.1:9876");
	producer.setCompressMsgBodyOverHowmuch(16);

	// 省略构造测试消息的代码

	producer.start(); 

	// 省略用于测试构造 mock 的代码

}

この初期化コードのロジックは非常に単純です。DefaultMQProducerのインスタンスを作成し、そのいくつかのパラメーターを初期化してから、startメソッドを呼び出して開始します。

次に、startメソッドの実装をフォローアップしその初期化プロセスの分析を続けます。
DefaultMQProducerImpl#start()メソッドはDefaultMQProducer#start()メソッドで直接呼び出されます。このメソッドのコードを直接見てみましょう。

ここで、RocketMQはメンバー変数serviceStateを使用して、独自のサービス状態を記録および管理します。これは、実際には状態パターンデザインパターンのバリアント実装です。
状態パターンにより、オブジェクトは内部状態が変化したときに動作を変更できます。オブジェクトはクラスを変更したように見えます。

標準の状態モードとは異なり、状態サブクラスを使用しませんが、switch-caseを使用してさまざまな状態でさまざまな動作を実装します。比較的単純な状態を管理する場合、この設計を使用すると、コードがより簡潔になります。このモードは、ステートフルクラスの管理に非常に広く使用されているため、日常の開発で使用することをお勧めします。
状態を設計するとき、注意を払う必要がある2つの主要なポイントがあります。

1つ目は、正常状態を設計するだけでなく、中間状態異常状態を設計することです。そうしないと、システムが異常になると、ステータスが正確でなくなり、この異常状態に対処するのが非常に困難になります。たとえば、このコードでは、RUNNINGとSHUTDOWN_ALREADYは正常な状態、CREATE_JUSTは中間状態、START_FAILEDは異常な状態です。
2つ目のポイントは、これらの状態間の遷移パスを明確に検討し、状態遷移を実行するときに、前の状態が次の状態に遷移できるかどうかを確認することです。たとえば、ここでは、CREATE_JUST状態でのみ、RUNNING状態に変換できるため、サービスが1回だけで、1回しか開始できないことが保証されます。これにより、サービスを複数回開始することによって発生するさまざまな問題を回避できます。
次に、起動プロセスの実装を見てみましょう

1.単一モードの実施形態MQClientManager取得MQClientInstance例mQClientFactoryを、自動的に新しいインスタンスを作成しない;
2 mQClientFactoryに自身を登録する;
3.スタートmQClientFactory;
全ブローカに送信4.ハートビート。

ここでも、最も単純なデザインパターンの1つを使用します。シングルトンパターン(シングルトンパターンには単一のクラスが含まれます。このクラスは、単一のオブジェクトのみが作成されるようにしながら、独自のオブジェクトを作成する責任があります。このクラスは、独自のTheにアクセスする方法を提供します。このクラスのオブジェクトをインスタンス化する必要なしに、オブジェクトのメソッドに直接アクセスできます。

対応するクラスMQClientInstanceクライアントのRocketMQトップレベルクラスである例mQClientFactory、ほとんどの場合、クラスMQClientInstanceのインスタンス内の各クライアントに対応するものとして単純に解釈できます。このインスタンスは、クライアントの状態情報のほとんどと、プロデューサー、コンシューマー、およびさまざまなサービスのすべてのインスタンスを保持します。クライアントの全体的な構造を学びたい学生は、このクラスの分析から始めて、分析を徐々に洗練することができます。 。
MQClientInstance#start()のコードをさらに分析してみましょう。

コードのこの部分のコメントは比較的明確であり、プロセスは次のようになります。

1.インスタンスmQClientAPIImplを開始します。mQClientAPIImplはクラスMQClientAPIImplのインスタンスであり
クライアントとブローカー間の通信方法をカプセル化します。2。ブローカーとのハートビートのタイミング、ネームサーバーとのデータ同期のタイミングなど、さまざまなタイミングタスクを開始します。タスク;
3。プルメッセージサービスを開始します;
4。リバランスサービスを
開始します; 5。デフォルトのプロデューサーサービスを開始します。

上記はプロデューサーの起動プロセスです。

いくつかの重要なクラスがあり、それぞれの責任について明確にする必要があります。後でRocketMQを使用するときに、問題が発生してコードをデバッグする必要がある場合は、これらの重要なクラスの責任を理解することが非常に役立ちます。

1. DefaultMQProducerImpl:プロデューサーの内部実装クラス。プロデューサーのビジネスロジック、つまりメッセージ送信のロジックのほとんどは、このクラスにあります。
2. MQClientInstance:このクラスは、プロデューサーかコンシューマーかに関係なく、クライアントの一般的なビジネスロジックをカプセル化します。最終的にサーバーと対話する必要がある場合は、このクラスのメソッドを呼び出す必要があり
ます。3。MQClientAPIImpl:このクラスはクライアントサーバー側のRPCは、実際のネットワーク通信部分の特定の実装を
呼び出し元から隠します。4。NettyRemotingClient:RocketMQプロセス間のネットワーク通信の基盤となる実装クラス。

3、メッセージ送信プロセス

次に、プロデューサーがメッセージを一緒に送信するプロセスを分析します。
ProducerインターフェースMQProducerでは、さまざまなパラメーターを使用してメッセージを送信する19の方法が定義されており、さまざまな送信方法に応じて3つのカテゴリーに分類できます。

  • 一方向送信(一方向):メッセージを送信した直後に戻り、応答を処理せず、送信が成功したかどうかを気にしません。
  • 同期送信(同期):メッセージの送信後に応答を待機します。
  • 非同期送信(非同期):メッセージを送信した直後に戻り、提供されたコールバックメソッドで応答を処理します。

3種類の送信は基本的に同じで、非同期送信は少し異なります。非同期送信「DefaultMQProducerImpl#send()」(ソースコードの行1132に対応)の実装方法を見てみましょう。

RocketMQがExecutorServiceを使用して非同期送信を実現していることがわかります。asyncSenderExecutorのスレッドプールを使用し、メソッドsendSelectImpl()を非同期的に呼び出し、メッセージ送信のフォローアップ作業を続行します。現在のスレッドは送信タスクをasyncSenderExecutorに送信します。戻り値。一方向送信と同期送信の実現は、現在のスレッドでメソッドsendSelectImpl()を直接呼び出すことです。
メソッドsendSelectImpl()の実装を引き続き見みましょう

メインの関数方法sendSelectImpl()が送信されるキューを選択することであり、次いで方法sendKernelImpl(呼び出し)メッセージを送信します
送信するために選択するキューは、MessageQueueSelector#selectメソッドによって決定されますここで、RocketMQは戦略パターン(戦略パターン)を使用して、さまざまなシナリオに対処するには、さまざまなキュー選択アルゴリズムの問​​題が必要です。(戦略モード:一連のアルゴリズムを定義し、各アルゴリズムをカプセル化して、それらを相互に置き換えることができます。戦略モードでは、アルゴリズムを使用する顧客とは無関係にアルゴリズムを変更できます)
RocketMQはランダムなど、MessageQueueSelectorの多くの実装を提供します選択戦略ha戦略同じコンピュータルーム選択戦略などを選択したい場合は、必要に応じて、選択戦略を自分で実装することもできます前のコースで説明したように、同じキーを持つメッセージの厳密な順序を確保する場合は、ハッシュ選択戦略を使用するか、自分で実装する選択戦略を提供する必要があります。
次に、sendKernelImpl()メソッドを見てみましょうこのメソッドには多くのコードがあり、約200行ですが、ロジックは比較的単純です。主な関数、ヘッダーRequestHeaderContext SendMessageContextを実行してから、メソッドMQClientAPIImpl#sendMessage()呼び出して、キューが配置されているブローカーにメッセージを送信します。
この時点で、メッセージはパッケージクラスMQClientAPIImplに送信され、リモート呼び出しが行われて、後続のシリアル化とネットワーク送信の手順完了します。

RocketMQ Producerのメッセージ送信プロセス全体が、同期送信か非同期送信かに関係なく、同じプロセスに統合されていることがわかりますメッセージの非同期送信の実現を含め、実際にはスレッドプールを介して実現されます。この場合、非同期スレッドで実行される呼び出しと同期送信は同じ基本メソッドです。コード基本メソッドメソッドのパラメータは、同期と非同期の送信を区別するために使用されますこの実装の利点は次のとおりです。プロセス全体が統合され、多くの同期および非同期の共通ロジックがあり、コードを再利用でき、コード構造が明確でシンプルであり、保守が容易です。

  • 同期送信を使用する場合、現在のスレッドはサーバーからの応答の待機をブロックし、応答を受信するかタイムアウトメソッドを受信するまで戻りません。したがって、ビジネスコードが同期送信を呼び出す場合、メッセージは正常に送信される必要があります。返品が成功する限り。
  • 非同期で送信する場合、送信ロジックはエグゼキュータの非同期スレッドで実行されるため、現在のスレッドはブロックされません。サーバーが応答を返すかタイムアウトすると、プロデューサーはCallbackメソッドを呼び出して結果をビジネスに返します。コード。ビジネスコードは、コールバックで送信結果を判断する必要があります。

総括する:

Producerの初期化とメッセージ送信のメインプロセスを含む、RocketMQクライアントメッセージ生成の実現プロセス。

プロデューサーに含まれるいくつかのコアサービスはすべてステートフルです。プロデューサーが起動すると、MQClientInstanceクラスで開始さます。

メッセージの送信プロセスでは、RocketMQは一方向同期非同期の3つの送信メソッドに分けられます。これら3つの送信メソッドに対応する送信プロセス基本的に同じです。同期送信と非同期送信は、カプセル化 されたMQClientAPIImplクラスによって分離されます。 。

分析コードで説明したいくつかの重要なビジネスロジック実装クラスについては、次のようなこれらのクラスとその関数を覚えておくとよいでしょう。DefaultMQProducerImplはプロデューサーのビジネスロジックのほとんどをカプセル化し、MQClientInstanceはクライアントをカプセル化します。一般的なビジネスロジック、MQClientAPIImplはクライアントとサーバー間のRPC、およびNettyRemotingClientは、基盤となるネットワーク通信を実装します。

もちろん、これはメインプロセスであり、詳細を示してから、ソースコードを確認します。

 

おすすめ

転載: blog.csdn.net/ScorpC/article/details/114225699