コンシューマーで例外が発生した場合、ApsaraMQ for RocketMQ は消費リトライポリシーに基づいてメッセージを再配信し、障害回復を実行します。このトピックでは、消費リトライ機能のユースケース、動作メカニズム、バージョン互換性、および使用上の推奨事項について説明します。
シナリオ
ApsaraMQ for RocketMQ の消費リトライは、主にビジネス処理ロジックの障害によってメッセージが正常に消費されない問題に対処します。これはお客様のビジネスのためのフォールバック戦略であり、ビジネスフロー制御には使用しないでください。
-
次のシナリオではメッセージリトライを使用します:
-
業務処理が失敗し、その原因が現在のメッセージ内容に関連している場合。たとえば、このメッセージのトランザクション解決がまだ取得できていないものの、短時間の遅延後に成功が見込まれる場合。
-
消費失敗の原因がシステム起因ではない場合。つまり、一貫して失敗するのではなく、まれな事象により現在のメッセージのみが失敗し、後続メッセージは成功する可能性が高い場合。この場合、現在のメッセージをリトライすることで処理のブロックを回避できます。
-
-
次のシナリオではメッセージリトライを避けてください:
-
消費失敗を処理ロジックの条件分岐として使用するのは不合理です。ロジックがすでにこの分岐の頻繁な発生を想定しているためです。
-
消費失敗を使用してレート制限を実装するのは不適切です。レート制限の目的は、過剰なトラフィックを一時的にキューイングしてピークシェービングを行うことであり、メッセージをリトライ経路に流すことではありません。
-
目的
メッセージ指向ミドルウェアを非同期デカップリングに用いると、ダウンストリームサービスでのメッセージ処理が失敗した場合に、呼び出しチェーンの整合性をいかにして確保するかが課題となります。ApsaraMQ for RocketMQ は、金融グレードで信頼性の高いビジネスメッセージ指向ミドルウェアとして、そのメッセージ配信処理メカニズムに信頼性の高い伝送戦略をサポートする設計が本来組み込まれています。完全な確認応答と再試行メカニズムにより、すべてのメッセージがビジネスの期待どおりに処理されることを保証します。
ApsaraMQ for RocketMQ のメッセージ確認応答メカニズムと消費リトライポリシーを理解することで、次の問題を分析できます。
-
メッセージ処理の完全性をどのように保証するか:リトライポリシーを把握することで、すべてのメッセージが完全に処理されるようにコンシューマーロジックを設計でき、メッセージの処理漏れやビジネス状態の不整合を防止できます。
-
システム障害時にメッセージ状態をどのように復旧するか:システム異常 (例:障害) の発生時に Inflight のメッセージ状態がどのように復元されるか、状態不整合が発生する可能性があるかを明確に理解できます。
消費リトライポリシー
消費リトライポリシーは、コンシューマーがメッセージの処理に失敗した後のリトライ間隔と最大リトライ回数を定義します。
リトライのトリガー
-
消費失敗。失敗ステータスの返却、または予期しない例外のスローを含みます。
-
メッセージ処理のタイムアウト。PushConsumer のキュータイムアウトを含みます。
主なリトライ動作
-
リトライ用ステートマシン:リトライ中のメッセージ状態と遷移を制御します。
-
リトライ間隔:消費失敗 (またはタイムアウト) から、メッセージが再消費可能になるまでの時間です。
-
最大リトライ回数:メッセージをリトライできる最大回数です。
メッセージリトライポリシーの違い
リトライメカニズムと設定方法は、コンシューマータイプによって次のように異なります:
|
コンシューマータイプ |
リトライ用ステートマシン |
リトライ間隔 |
最大リトライ回数 |
|
PushConsumer |
|
コンシューマーグループ作成時のメタデータで制御されます。
|
コンソールまたは OpenAPI で設定します |
|
SimpleConsumer |
|
API でメッセージを取得する際に InvisibleDuration を設定します。 |
コンソールまたは OpenAPI で設定します |
詳細なリトライポリシーについては、「PushConsumer の消費リトライポリシー」および「SimpleConsumer の消費リトライポリシー」をご参照ください。
PushConsumer の消費リトライポリシー
リトライ用ステートマシン
PushConsumer がメッセージを処理する際、メッセージは次の状態を遷移します:
Ready:消費準備完了状態です。
メッセージは ApsaraMQ for RocketMQ サーバー上で準備が完了しており、コンシューマーによる消費が可能です。
Inflight:処理中状態です。
メッセージはコンシューマークライアントによって取得され、処理中ですが、まだ消費結果を返していません。
WaitingRetry:保留中のリトライ状態で、PushConsumer 固有の状態です。
メッセージ処理が失敗またはタイムアウトした場合にトリガーされます。現在のリトライ回数が最大リトライ回数に達していない場合、メッセージは WaitingRetry 状態に入ります。リトライ間隔の経過後、再消費のために Ready 状態に戻ります。永続的な障害で高頻度のリトライが発生することを防ぐため、リトライ間隔は試行回数に応じて増加します。
Commit:コミット状態です。
消費が成功したことを示します。コンシューマーが成功応答を返すと、メッセージのステートマシンは終了します。
DLQ:デッドレターキューです。
デッドレター状態は、最終的なフォールバックです。リトライが最大回数を超え、デッドレターメッセージの保持が有効になっている場合、失敗したメッセージはデッドレター トピックに送信されます。このトピックからメッセージを消費して、業務を復旧できます。詳細については、「デッドレターメッセージ」をご参照ください。
Discard:破棄状態です。
リトライが最大回数を超え、デッドレター保持が無効の場合、メッセージは破棄されます。

例:上図では、メッセージが Ready 状態に 5 秒間留まり、処理に 6 秒かかるものとします。
各リトライサイクルは Ready → Inflight → WaitingRetry に従います。リトライ間隔は、失敗 (またはタイムアウト) からメッセージが再び Ready になるまでの時間です。2 回の消費試行の間の実際の時間には、処理時間と Ready 状態での滞在時間も含まれます。例:
-
0 秒で、メッセージが Ready 状態に入ります。
-
コンシューマーの処理速度により、消費は 5 秒で開始します。6 秒後 (11 秒時点) に例外が発生し、クライアントは失敗を返します。
-
リトライは直ちに開始できず、リトライ間隔を待つ必要があります。
-
21 秒で、メッセージが再び Ready 状態になります。
-
クライアントは 5 秒後に再消費を開始します。
したがって、2 回の消費試行の実際の間隔は、処理時間 + リトライ間隔 + Ready 状態での滞在時間 = 21 秒です。
リトライ間隔
-
非順序メッセージ (順序なしメッセージ) :リトライ間隔はステップ方式を使用します。次のとおりです:
リトライ回数
リトライ間隔
リトライ回数
リトライ間隔
1
10 秒
9
7 分
2
30 秒
10
8 分
3
1 分
11
9 分
4
2 分
12
10 分
5
3 分
13
20 分
6
4 分
14
30 分
7
5 分
15
1 時間
8
6 分
16
2 時間
説明リトライ回数が 16 回を超える場合、以降のすべてのリトライは 2 時間間隔になります。
-
順序付きメッセージ:固定のリトライ間隔を使用します。具体的な値については、「パラメーター制限」をご参照ください。
最大リトライ回数
デフォルト値:16。
最大制限:1,000。
PushConsumer の最大リトライ回数は、コンシューマーグループのメタデータで制御されます。変更方法については、「最大リトライ回数の変更」をご参照ください。
たとえば最大リトライ回数が 3 の場合、メッセージは最大 4 回配信されます。初回配信が 1 回、リトライが 3 回です。
使用例
PushConsumer でリトライをトリガーするには、消費失敗のステータスコードを返すだけです。予期しない例外は SDK が自動的にキャッチします。
SimpleConsumer simpleConsumer = null;
// 消費例:PushConsumer を使用して通常メッセージを消費します。失敗時にエラーを返してリトライをトリガーします。
MessageListener messageListener = new MessageListener() {
@Override
public ConsumeResult consume(MessageView messageView) {
System.out.println(messageView);
// FAILURE を返すと、最大リトライ回数に達するまで自動的にリトライされます。
return ConsumeResult.FAILURE;
}
};
消費リトライログの確認
順序付きメッセージの場合、PushConsumer はクライアント側でリトライを実行します。サーバーは詳細なリトライログにアクセスできません。メッセージトレースで順序付きメッセージの配信失敗が表示される場合は、最大リトライ回数とクライアント情報について、コンシューマークライアントログを確認してください。
クライアントログのパスについては、「ログ設定」をご参照ください。
クライアントログで次のキーワードを検索すると、消費失敗の詳細をすばやく特定できます:
Message listener raised an exception while consuming messages
Failed to consume fifo message finally, run out of attempt times
SimpleConsumer の消費リトライポリシー
リトライ用ステートマシン
SimpleConsumer がメッセージを処理する際、メッセージは次の状態を遷移します:
Ready:消費準備完了状態です。
メッセージは ApsaraMQ for RocketMQ サーバー上で準備が完了しており、コンシューマーによる消費が可能です。
Inflight:処理中状態です。
メッセージはコンシューマークライアントによって取得され、処理中ですが、まだ消費結果を返していません。
Commit:コミット状態です。
消費が成功したことを示します。コンシューマーが成功応答を返すと、メッセージのステートマシンは終了します。
DLQ:デッドレターキューです。
デッドレター状態は、最終的なフォールバックです。リトライが最大回数を超え、デッドレターメッセージの保持が有効になっている場合、失敗したメッセージはデッドレター トピックに送信されます。このトピックからメッセージを消費して、業務を復旧できます。詳細については、「デッドレターメッセージ」をご参照ください。
Discard:破棄状態です。
リトライが最大回数を超え、デッドレター保持が無効の場合、メッセージは破棄されます。
PushConsumer とは異なり、SimpleConsumer は事前に割り当てられたリトライ間隔を使用します。メッセージを取得する際、コンシューマーは許容される最大処理時間である InvisibleDuration パラメーターを設定します。失敗した場合、次のリトライ間隔では追加の設定なしでこの値が再利用されます。

InvisibleDuration は事前割り当てであるため、実際の処理時間と大きく異なる場合があります。API で変更できます。
たとえば、処理時間を 20 ms に設定したものの実際の処理がこれを超える場合、早すぎるリトライを回避するために InvisibleDuration を延長してください。
InvisibleDuration を変更するには、次の条件を満たす必要があります:
-
メッセージ処理がタイムアウトしていないこと。
-
消費ステータスがコミットされていないこと。
次に示すとおり、新しい InvisibleDuration は直ちに有効になり、API 呼び出し時点から非表示タイマーが再開されます。

リトライ間隔
リトライ間隔 = InvisibleDuration - 実際の処理時間
SimpleConsumer は InvisibleDuration によってリトライ間隔を制御します。たとえば、InvisibleDuration が 30 ms で、処理が 10 ms 後に失敗した場合、次のリトライは 20 ms 後に発生します。処理が 30 ms 以内に完了せず結果が返されない場合、メッセージはタイムアウトし、直ちにリトライされます (間隔 0 ms)。
最大リトライ回数
デフォルト値:16。
最大制限:1,000。
SimpleConsumer の最大リトライ回数は、作成時のコンシューマーグループメタデータで制御されます。変更方法については、「最大リトライ回数の変更」をご参照ください。
たとえば最大リトライ回数が 3 の場合、メッセージは最大 4 回配信されます。初回配信が 1 回、リトライが 3 回です。
使用例
SimpleConsumer でリトライをトリガーするには、確認応答を返さずにメッセージをタイムアウトさせます。
// 消費例:SimpleConsumer を使用して通常メッセージを消費します。リトライをトリガーするには、何も返さずメッセージをタイムアウトさせます。サーバーが自動的にリトライします。
List<MessageView> messageViewList = null;
try {
messageViewList = simpleConsumer.receive(10, Duration.ofSeconds(30));
messageViewList.forEach(messageView -> {
System.out.println(messageView);
// 失敗時はメッセージを無視します。メッセージは再び可視になり、リトライされます。
});
} catch (ClientException e) {
// スロットリングやその他のシステム問題により pull が失敗した場合は、receive リクエストをリトライします。
e.printStackTrace();
}
最大リトライ回数の変更
次の方法で、PushConsumer および SimpleConsumer の最大リトライ回数を変更できます。
1. クライアントが Remoting プロトコルを使用している場合、実際の最大リトライ回数はクライアント側の設定に従い、ここでの設定は反映されません。クライアントが gRPC プロトコルを使用している場合は、ここでの設定が適用されます。
2. リトライ戦略 (エクスポネンシャルバックオフまたは固定間隔) は gRPC クライアントにのみ適用され、Remoting クライアントには影響しません。
gRPC SDK
-
OpenAPI で変更:コンシューマーグループの更新
-
コンソール経由で変更:
設定にアクセスするには:
-
インスタンス数 ページで、対象のインスタンス名をクリックします。
-
左側のナビゲーションペインで、グループ をクリックします。グループ ページで、グループの作成 をクリックします。
[Create Group] ダイアログボックスで、[Group ID] (1~60 文字)、[Delivery Order] ([Concurrent Delivery] または [Ordered Delivery])、および [Description] を設定します。[Advanced Settings] を展開して、リトライポリシーを [Exponential Backoff] に設定し、[Maximum Retry Count] (デフォルト:16) を設定し、[Retain Dead-letter Messages] (デフォルト:オフ。オフの場合、リトライ回数を超えたメッセージは破棄されます) を切り替えます。
-
Remoting SDK
-
Remoting SDK パラメーターで変更:コンシューマーの maxReconsumeTimes プロパティを設定します。
ベストプラクティス
リトライの適切な運用:レート制限にリトライを使用しない
「シナリオ」で述べたとおり、メッセージリトライは、まれな業務上の失敗に適しており、レート制限のようなシステム的または継続的な失敗には適していません。
-
誤った例:
消費レートがスロットリングを引き起こした場合、失敗を返してリトライを待ちます。
-
正しい例:
消費レートがスロットリングを引き起こした場合、メッセージ取得を遅延させ、後で消費します。
メッセージリトライに関するよくある質問
メッセージ消費タイムアウトの設定方法
gRPC プロトコル
-
SimpleConsumer:タイムアウト範囲は 10 秒~12 時間です。
コード例:
private long minInvisiableTimeMillsForRecv = Duration.ofSeconds(10).toMillis(); private long maxInvisiableTimeMills = Duration.ofHours(12).toMillis(); -
PushConsumer:デフォルトは 230 分で、変更できません。
Remoting プロトコル
consumer.setConsumeTimeout(15); // 単位:分。範囲:1~180 分