4

一貫性の保証をきめ細かく制御できるメッセージバスの実装を知っている人はいますか? 完全な ACID は遅すぎますが、ACID はどれも間違っていません。

現在、メッセージには Rhino ESB ラッピング MSMQ を使用しています。分散トランザクションで永続的なトランザクション メッセージングを使用する場合、MSMQ は、I/O の完了を待機する間、かなりの時間コミットをブロックする可能性があります。

私たちのメッセージは、ビジネス ロジックと非正規化という 2 つの一般的なカテゴリに分類されます。後者は、メッセージ バス トラフィックのかなりの割合を占めています。

ビジネス ロジック メッセージには完全な ACID の保証が必要であり、MSMQ はこれに十分適していることが証明されています。

非正規化メッセージ:

  1. 耐久性がなければなりません。
  2. 元のトランザクションが完了するまで処理してはなりません。
  3. 複数回処理される場合があります。
  4. 2) が守られている限り、元のトランザクションがロールバックされても処理される場合があります。

(いくつかの特定のケースでは、おそらく耐久性の要件が緩和される可能性がありますが、それらのケースをルールの例外として特定して処理すると、複雑さが増します。)

非正規化メッセージはすべてインプロセスで処理されるため、IPC は必要ありません。

プロセスが再開された場合、すべてのトランザクションが完了 (コミットまたはロールバック) したと見なされ、まだ処理されていないすべての非正規化メッセージを回復する必要があります。すでに処理された非正規化メッセージを再生することは許容されます。

私が知る限り、トランザクションを処理するメッセージング システムは、完全な ACID を使用するか、何も使用しないかの選択肢を提供する傾向があり、ACID はパフォーマンスの低下をもたらします。送信されたメッセージの数によっては、TransactionScope#Commit() の呼び出しに数百ミリ秒かかる場合があります。

非トランザクション メッセージ キューを使用すると、元のトランザクションが完了する前にメッセージが処理されるため、一貫性の問題が発生します。

同様の一貫性要件を持つが複雑さの低いシステムの別の部分では、トランザクション ログに似たもののカスタム実装を既に使用しています。 、同時実行、耐久性、トランザクショナル メッセージング システムを自分で作成する必要がない場合:P

不思議に思っている人のために説明すると、非正規化メッセージの持続性を要求する理由は、非同期の検出と非同期の修正がそれぞれ非常に難しく、非常にコストがかかる可能性があるためです。何かがわずかに間違っていて、ページを更新しても修正されない場合、人々気付くので、非同期化を無視することはできません。

4

3 に答える 3

0

MSMQ+SQL+DTC では、必要な一貫性の保証さえ提供されないことが判明しました。以前、メッセージをキューに入れた分散トランザクションがデータベースにコミットされる前にメッセージが処理され、古い読み取りが発生するという問題が発生しました。これは、次の理由から、ReadCommitted 分離を使用してキューを消費することの副作用です。

  1. トランザクション A を開始します。
  2. A のデータベース テーブルを更新します。
  3. A のキュー メッセージ。
  4. A のコミットを要求します。
  5. メッセージキューコミット A
  6. トランザクション B を開始します。
  7. B のメッセージを読む。
  8. ReadCommitted <- A より前のデータを取得して、B のデータベース テーブルを読み取ります。
  9. データベース コミット A.

私たちの要件は、A のコミット時に B がテーブル ブロックを読み取ることです。これには、パフォーマンス ペナルティを伴うシリアライズ可能なトランザクションが必要です。

車輪の再発明のように聞こえますが、実際に必要な制約を実装して自分自身を保証することは、通常のことのようです。

これについて何かコメントはありますか?

于 2013-09-03T09:25:12.560 に答える
0

これを手動で行いたい場合は、信頼できる方法を次に示します。(1) と (2) を満たし、(3) と (4) で許可する自由さえ必要としません。

  1. プロデューサー (ビジネス ロジック) がトランザクション A を開始します。
  2. 1 つ以上のテーブルに何でも挿入/更新します。
  3. 対応するメッセージを PrivateMessageTable (ドメインの一部であり、必要に応じて非共有) に挿入します。これが配布されるものです。
  4. コミット トランザクション A. Producer は、メッセージの挿入を含む書き込みを簡単かつ確実に実行するか、すべてをロールバックしました。
  5. 専用ディストリビューター ジョブは、PrivateMessageTable から未処理のメッセージのバッチをクエリします。
  6. ディストリビューターはトランザクション B を開始します。
  7. 未処理のメッセージを処理済みとしてマークし、変更された行数が予想と異なる場合はロールバックします (2 つのインスタンスが同時に実行されていますか?)。
  8. メッセージのパブリック表現を PublicMessageTable (何らかの方法で公開されたテーブル) に挿入します。新しい厳密に連続した ID を公開表現に割り当てます。これらの挿入を行うプロセスは 1 つだけであるため、これは保証されます。2PC を回避するには、テーブルが同じホスト上にある必要があることに注意してください。
  9. トランザクション B をコミットします。ディストリビューターは、厳密に連続した ID を使用して、各メッセージをパブリック テーブルに 1 回だけ配信しました。
  10. コンシューマー (複数存在する可能性があります) は、自身の LastSeenId より大きい ID を持つ PublicMessageTable からメッセージの次のバッチをクエリします。
  11. コンシューマーがトランザクション C を開始します。
  12. コンシューマーは、メッセージの独自の表現を独自のテーブル ConsumerMessageTable に挿入します (したがって、LastSeenId を進めます)。Insert-ignore は、実行中の複数のインスタンスから保護するのに役立ちます。このテーブルは、まったく別のサーバーにある可能性があることに注意してください。
  13. トランザクション C をコミットします。消費者は、メッセージをスキップすることなく、メッセージが公開されたのと同じ順序で、各メッセージを 1 回だけ消費しました。
  14. 消費されたメッセージに基づいて、やりたいことが何でもできます。

もちろん、これには非常に慎重な実装が必要です。

書き込みノードが 1 つしかなく、読み取りと書き込みの両方で因果関係のチェックが実行される限り、データベース クラスターにも適しています。これらのいずれかを持っていれば十分かもしれませんが、その主張を行うには、その意味をより慎重に検討する必要があります.

于 2018-09-19T10:07:09.687 に答える