2017-05-18 8 views
1

リトライ・コンテキスト内で、Springリスナー・コンテナがトランザクションをどのように処理するかを理解しようとしていました。Springリスナー・コンテナ(トランザクション・マネージャとリトライ・アドバイス・オーダー)

私はこのようなものに構成:

<rabbit:listener-container connection-factory="connectionFactory" 
          transaction-manager="chainedTransactionManager" 
          channel-transacted="true" 
          advice-chain="retryAdvice"> 
    <rabbit:listener ref="myMessageProcessor" queue-names="test.messages" method="handleMessage"/> 
</rabbit:listener-container> 

をそして私は、トランザクションが再試行の中に含まれることを期待していた、私の取引が何らかの理由で失敗した場合、私は特定の例外を再試行することを決定することができるようにし、他の人には私のDLQにメッセージを送るだけです。

しかし、私は再試行コードがトランザクションコード内に含まれていることに驚いていました。言い換えれば

、春のリスナーがあるようです:

doWithRetry - doIntransaction -> invokeMyCode 

私の計画はJpaTransactionManagerとAの両方を含むChainedTransactionManagerを使用していた:

doIntransaction -> doWithRetry -> invokeMyCode 

私はそれはこのようになります期待していましたRabbitTransactionManagerここでは、私が読んだメッセージの承認と、このトランザクション中に送信したメッセージのコミットと特定の条件によってトランザクション全体を再試行することができますが、これはw ay。

だけでなく、トランザクション内で例外が発生した後、コンテキストが役に立たなくなる可能性があります。私は再試行が意味をなさせるために新しい取引が必要です。

また、コミット/ロールバックフェーズで発生した例外は、再試行コンテキスト外で発生するため、再試行されないという問題があります。私は彼らがErrorHandlerの設定に応じて再試行されたと仮定し、私の勧告されたコードに基づいていません。不幸にも、ErrorHandlerにはバックオフポリシーがありません。つまり、トランザクションを再試行した回数をカウントする便利なRetryContextです。

この場合のように、トランザクションマネージャーでリスナーを構成して機能を再試行するには、どのような方法が推奨されますか?

答えて

1

私はそれを試したことはありませんが、コンテナからトランザクションマネージャを削除し、通常のSpring TransactionInterceptorをアドバイスチェーンに追加して(リトライアドバイス後に)望ましい動作を達成できるはずです。

コンテナにトランザクションマネージャがある場合は、リスナ(アドバンテージチェーンでラップされています)を呼び出す前にトランザクションを開始するように指示しています。

しかし、ローカルトランザクションを使用していると思っているため、コンテナはおそらく配信を確認/コミットしようとします(インターセプタが既に行っていた場合、 RabbitTransactionManagerが設定されています)。

chainedTransactionManagerRabbitTransactionManagerを含めない限り、これは起こりません。コンテナは単にローカルトランザクションを使用します。

RTMを含める場合は、手動アークを使用するか、ダミートランザクションマネージャをコンテナに追加する必要があります。

あなたの作り方を教えてください。私は明日見ることができます。

ステートフル再試行を使用して、以下に説明するようにEDITは

メッセージは拒否され、再配信されているので、簡単な解決策です。しかし、messageIdヘッダー(またはカスタムキージェネレーター)が必要です。

+0

提案してくれてありがとう、ゲーリー。私はあなたのアイデアをまだ試していませんでした。なぜなら、もう少し気に入っているようなアイデアが出てきたからです。自分の答えを見て、それがあなたに何か意味があるかどうかを見ることができますか?これが問題になるかもしれないと思ったら、私は助言に感謝します。私は一日中、問題の回避策を見つけようと、Spring AMQPコードを読んできました。 –

+1

はい。それはうまくいく - あなたが容器にRabbitTMを必要としなくても、単にローカルに処理されたチャンネルを使うことができる - 我々は依然として 'RabbitTemplate'によって下流使用のためにチャンネルを(デフォルトで)バインドする。再試行が成功した場合、複数の送信がコミットされていることが分かります。 '@RabbitListener'の中で、JPAを処理するための' @ Transactional'ビーンを呼び出し、次に送信を行うことで、明示的なトランザクションテンプレートを避けることができます。 –

+1

こんにちはゲイリー、私はステートフルな再試行を使用すると、トランザクション全体が常に再開され、私の問題を解決すると思います。私はおそらくもっと簡単だと思います。そのため、リスナーでchannelTransactedを実行し、ステートフルなリトライを使用するretryAdviceがそのトリックを行う必要があります。 –