10

私の問題を単純化するために、私は持っています

@Transactionnal メソッド createUser() を使用する App1:

  • データベースに新しいユーザーを挿入する
  • ユーザーが通知メールを受信できるように、RabbitMQ に非同期メッセージを追加します。
  • (潜在的にいくつかの追加コードですが、それほど多くはありません)

App2 と RabbitMQ メッセージ コンシューマ

  • メーリング キューのメッセージをリアルタイムで消費します
  • データベース内のメール データを読み取る
  • メールを送る

問題は、App1 でトランザクションがコミットされる前に、App2 が RabbitMQ メッセージを消費しようとする場合があることです。これは、ユーザーがまだ作成されていないため、App2 がデータベース上のメール データを読み取ることができないことを意味します。

いくつかの解決策は次のとおりです。

  • App2 で READ_UNCOMMITED 分離レベルを使用する
  • RabbitMQ メッセージの配信に遅延を追加します (またはコンシューマーで RetryTemplate を追加します)。
  • メールの送り方を変え...

Spring に RabbitTransactionManager があるのを見たことがありますが、それがどのように機能するのか理解できません。トランザクション処理の内部は常に理解しにくいように思われ、ドキュメントもあまり役に立ちません。


このようなことをする方法はありますか?

  • @Transactionnal メソッドで RabbitMQ キューにメッセージを追加する
  • トランザクションが終了すると、メッセージがキューにコミットされ、変更がデータベースにコミットされます
  • DBトランザクションが終了する前にメッセージが消費されないようにする

どのように?たとえば、非同期メッセージの代わりに同期 RabbitMQ メッセージを送信した場合、どうなるでしょうか? 応答または何かを待っているスレッドをブロックしますか? さまざまなユースケースで同期メッセージと非同期メッセージを送信するためです。

4

2 に答える 2

3

これが遅れていることはわかっていますが、当時の @Transactional の理解が限られていたため、同じ問題が発生しました。したがって、これは、たまたまこれに出くわした他の人にとってより重要です。

@Transactional を使用してデータをデータベースに保存する場合、データベースへの保存はメソッドが返されるまで実際には行われず、保存が呼び出されたときではありません。

したがって、次のようなメソッドがある場合

@Transactional(readOnly=false)
public void save(Object object) { //Object should be one of your entities
  entityManager.persist(object); //or however you have it set up
  rabbitTemplate.convertAndSend(message); //again - however yours is
}

メッセージをキューに入れる前にオブジェクトの永続化を呼び出しても、メソッドが戻るまで永続化は実際には行われないため、メソッドが戻る前とデータが実際に入る前にメッセージがキューに置かれます。データベース。

@Transactional メソッドをネストすることは可能ですが (簡単ではありません)、save()メソッドが返された後にメッセージをキューに入れることができます。ただし、メッセージをキューに入れても、それが消費されないことを期待することはできません。そこがなくなったら。そのため、必要に応じてキューに入れるのを遅らせてください。

キューからの応答を同期的に受け取りたい場合。私の関数の例では、これを行うことができますが、メソッドが戻って実際にデータを永続化する前にワーカーからの応答を待機するため、実際にデータを永続化するのに時間がかかるだけです。(キューに入れられたメッセージからの応答の受信にはタイムアウトがあることにも注意してください)。

したがって、私のアドバイスは、これら 2 つの操作を同じ @Transactional に入れないことです。

于 2016-02-05T16:30:30.403 に答える
1

@Transactionnal と spring にはあまり詳しくありませんが、AMQP では標準のメッセージ キューイングはトランザクション操作ではないため、データを db に保存し (db 接続がトランザクションの場合 - トランザクションをコミットする)、その後でのみメッセージをブローカーに送信する必要があります。

正しいワークフローは私のように見えますApp1: createUser -> notifyUser; App2: listenForNotifications

于 2013-07-03T10:31:49.833 に答える