コンシューマーグループ内の複数のコンシューマーがトピックからメッセージをプルすると、ApsaraMQ for RocketMQ は負荷分散ポリシーを使用して、それらのメッセージをコンシューマー間に分散します。これにより、スループットが向上し、水平スケーリングが簡素化されます。
負荷分散ポリシーの選択
ApsaraMQ for RocketMQ は 2 つの負荷分散ポリシーを提供しています。どちらが適用されるかは、コンシューマーのタイプとブローカーのバージョンによって決まります。
| ポリシー | デフォルトで適用 | 粒度 | 最適な用途 |
|---|---|---|---|
| メッセージベース | Push コンシューマー、Simple コンシューマー (ブローカー 5.x) | 個々のメッセージ | 各メッセージが独立して処理されるイベント駆動型のワークロード |
| キューベース | Pull コンシューマー (ブローカー 3.x/4.x/5.x) | キュー全体 | ストリーム処理およびバッチ集約のワークロード |
ヒント: コンシューマーがメッセージを 1 つずつ処理する場合、メッセージベースの負荷分散を使用すると、より均等な分散とシンプルな操作が実現します。コンシューマーが単一のソースからのメッセージを集約またはバッチ処理する場合は、キューベースの負荷分散を使用してください。
背景情報
これらのポリシーを理解することは、以下の計画を立てるのに役立ちます。
災害復旧:ローカルノードに障害が発生した場合のメッセージのリトライ方法とフェールオーバーの仕組み。
メッセージの順序性:ApsaraMQ for RocketMQ がメッセージグループ内で厳密な FIFO (先入れ先出し) 順序を維持する方法。
水平スケーリング:コンシューマーを追加または削除する際のトラフィック移行と水平スケーリングの計画方法。
ブロードキャスト消費とクラスター消費の比較
ApsaraMQ for RocketMQ は 2 つの消費モードをサポートしています。負荷分散はクラスター消費にのみ適用されます。
| モード | グループあたりのコンシューマー数 | メッセージ配信 | ユースケース | 負荷分散 |
|---|---|---|---|---|
| ブロードキャスト | グループごとに 1 つ | すべてのグループが全メッセージを受信 | ゲートウェイプッシュ、構成プッシュ | 適用外 |
| クラスター | グループごとに複数 | 各メッセージは 1 つのコンシューマーに送信 | マイクロサービスのデカップリング、水平スケーリング | メッセージベースまたはキューベース |

ブロードキャスト消費 (図の左側):各コンシューマーグループには、すべてのメッセージを受信するコンシューマーが 1 つあります。異なるコンシューマーグループは、それぞれ独立して完全なメッセージストリームを受信します。
クラスター消費 (図の右側):コンシューマーグループには複数のコンシューマーがあり、メッセージはそれらの間で分散されます。グループ内の 1 つのコンシューマーのみが各メッセージを処理します。
メッセージベースの負荷分散
仕組み
ブローカーは、メッセージが属するキューに関係なく、トピックからの個々のメッセージをコンシューマーグループ内のすべてのコンシューマーに均等に分散します。複数のコンシューマーが同じキューのメッセージを同時に処理できます。

この例では、コンシューマーグループ A には A1、A2、A3 の 3 つのコンシューマーがあります。3 つすべてが Queue1 からメッセージを消費します。ブローカーは各メッセージを一度に 1 つのコンシューマーに割り当てます。
コンシューマーがメッセージを受信すると、ブローカーはそのメッセージをロックし、他のコンシューマーから見えないようにします。メッセージは、コンシューマーが確認応答するか、ロックがタイムアウトするまでロックされたままになります。これにより、通常の条件下での重複処理が防止されます。
メッセージは事前割り当てではなく、オンデマンドで分散されます。特定のメッセージをどのコンシューマーが受信するかを制御することはできません。
順序付きメッセージの処理
順序付きメッセージの場合、ApsaraMQ for RocketMQ は、同じメッセージグループ内のメッセージがブローカーに保存された順序どおりに処理されることを保証します。

Queue1 のメッセージグループ G1 にある 4 つの順序付きメッセージ (M1 から M4) を考えます。コンシューマー A1 が M1 と M2 を処理している場合、コンシューマー A2 は、A1 が M1 と M2 の消費ステータスを送信するまで、M3 または M4 の処理を開始できません。ブローカーは、順序を維持するために厳密なシーケンシャルロックを強制します。
キューベースの負荷分散に対するメリット
| メリット | 仕組み | 重要性 |
|---|---|---|
| 均等な分散 | メッセージはオンデマンドで割り当てられるため、すべてのコンシューマーが常にビジー状態になります。 | キューベースの負荷分散では、キューとコンシューマーの数が一致しない場合、一部のコンシューマーがアイドル状態になる可能性があります。 |
| 不均等なキャパシティへの耐性 | 処理が遅いコンシューマーは、自動的に受信するメッセージが少なくなります。 | キューベースの負荷分散では、ネットワーク条件やハードウェア仕様の違いにより、ビジーなキューを割り当てられた遅いコンシューマーがバックログを蓄積する一方で、他のコンシューマーがアイドル状態になる可能性があります。 |
| 容量計画の簡素化 | キューの数をコンシューマーの数に一致させる必要がありません。 | キューの数を調整することなく、コンシューマーを自由に追加または削除できます。 |
利用シーン
メッセージベースの負荷分散は、注文処理、通知ディスパッチ、リアルタイムイベント処理など、各メッセージが独立して処理されるほとんどのオンラインイベント処理ワークロードに適しています。
同じキューからのメッセージをバッチ処理する必要があるストリーム処理や集約ワークロードの場合は、代わりにキューベースの負荷分散を使用してください。
適用範囲
メッセージベースの負荷分散は、ブローカーバージョン 5.x の Push コンシューマーおよび Simple コンシューマータイプで利用できる唯一のポリシーです。デフォルトで有効になっており、追加の構成は必要ありません。
例
Push コンシューマーと Simple コンシューマータイプは、メッセージベースの負荷分散を自動的に使用します。以下の Java の例は、両方のコンシューマータイプが負荷分散の構成なしでメッセージを処理する方法を示しています。
メッセージリスナーを持つ Push コンシューマー:
// Push コンシューマー:メッセージを処理するためにメッセージリスナーを実装します。
// 負荷分散はブローカーによって自動的に処理されます。
MessageListener messageListener = new MessageListener() {
@Override
public ConsumeResult consume(MessageView messageView) {
System.out.println(messageView);
// 消費結果を返します。
return ConsumeResult.SUCCESS;
}
};手動での確認応答を行う Simple コンシューマー:
// Simple コンシューマー:メッセージをプルし、処理し、それぞれを確認応答します。
// 負荷分散はブローカーによって自動的に処理されます。
try {
List<MessageView> messageViewList = simpleConsumer.receive(10, Duration.ofSeconds(30));
messageViewList.forEach(messageView -> {
System.out.println(messageView);
try {
// 処理後にメッセージを確認応答します。
simpleConsumer.ack(messageView);
} catch (ClientException e) {
e.printStackTrace();
}
});
} catch (ClientException e) {
// スロットリングやその他の問題でプルが失敗した場合は、リクエストをリトライします。
e.printStackTrace();
}キューベースの負荷分散
仕組み
ブローカーは、トピック内の各キューをコンシューマーグループ内のただ 1 つのコンシューマーに割り当てます。各コンシューマーは、割り当てられたキューからのすべてのメッセージを処理します。

この例では、トピックには 3 つのキュー (Queue1、Queue2、Queue3) があり、コンシューマーグループには 2 つのコンシューマーがあります。各キューは 1 つのコンシューマーに割り当てられるため、コンシューマー A2 は 2 つのキューを取得し、A1 は 1 つのキューを取得します。コンシューマーよりもキューが少ない場合、一部のコンシューマーはキューを受信せず、アイドル状態のままになります。
各コンシューマーは、割り当てられたキューからメッセージをプルし、消費オフセットを送信し、オフセットを永続化するという処理シーケンスに従います。コンシューマーがメッセージをプルする際に消費ステータスがキューに返されないため、重複処理を防ぐには、各キューを単一のコンシューマーに排他的に割り当てる必要があります。
キューベースの負荷分散は、各キューが 1 つのコンシューマーによって処理されるように設計されています。ただし、実装はコンシューマーとブローカー間の情報ネゴシエーションメカニズムに依存します。ApsaraMQ for RocketMQ は、キュー内のメッセージが 1 つのコンシューマーによってのみ処理されることを保証しません。コンシューマーまたはキューの数が変更されると、キューの割り当てに一時的な不整合が発生し、少数のメッセージが複数回処理される可能性があります。常にべき等なメッセージ処理を実装してください。
メッセージベースの負荷分散に対するメリット
| メリット | 仕組み | 重要性 |
|---|---|---|
| キューアフィニティ | キューからのすべてのメッセージが同じコンシューマーに送信されます。 | 単一のコンシューマー内でのローカル集約とバッチ処理を可能にします。 |
| ストリーム処理のサポート | コンシューマーは、同じキューからの連続したストリームに対してステートフルな処理を維持します。 | タイムウィンドウ計算と実行集計をサポートします。 |
利用シーン
キューベースの負荷分散は、同じソースからのメッセージをバッチ処理または集約する必要があるストリームコンピューティングやデータ集約アプリケーションに最適です。たとえば、コンシューマーがタイムウィンドウにわたって単一のキューからメトリックを収集し、実行平均を計算するような場合です。
適用範囲
キューベースの負荷分散は、Pull コンシューマー、デフォルトの Push コンシューマー、デフォルトの Pull コンシューマー、Lite Pull コンシューマータイプなど、ブローカーバージョン 3.x および 4.x のコンシューマーで利用できる唯一のポリシーです。ブローカーバージョン 5.x では、Pull コンシューマータイプはデフォルトでキューベースの負荷分散を引き続き使用します。
これらのコンシューマータイプでは、キューベースの負荷分散が自動的に有効になるため、追加の構成は必要ありません。
例
サンプルコードについては、Apache RocketMQ コードライブラリの LitePullConsumerAssign.java をご参照ください。
バージョンの互換性
| ブローカーバージョン | 利用可能なポリシー | 注意 |
|---|---|---|
| 3.x, 4.x | キューベースのみ | すべてのコンシューマータイプでキューベースの負荷分散が使用されます。 |
| 5.x | メッセージベースとキューベースの両方 | Push コンシューマーと Simple コンシューマーはデフォルトでメッセージベースを使用します。Pull コンシューマーはデフォルトでキューベースを使用します。 |
メッセージベースの負荷分散ポリシーは、ブローカーバージョン 5.0 で導入されました。ブローカーバージョン 5.x を実行している場合、アクティブなポリシーはクライアントのバージョンとコンシューマーのタイプによって異なります。
注意事項
べき等なメッセージ処理の実装
どちらの負荷分散ポリシーも、コンシューマーが追加、削除されたり、ブローカーがスケーリングされたりすると、一時的なリバランスをトリガーします。リバランス中、少数のメッセージが複数回配信される可能性があります。これに対処するには、重複排除を実装して、メッセージ消費ロジックのべき等性を確保してください。