これは、Javaアプリケーションでの2つの分散コンポーネントの一般的なシーケンスです。
1 A sends request to B
2 B starts some job J in parallel thread
3 B returns response to A
4 A accepts response
5 Job finishes after some time
6 Job sends information to A
7 A receives response from a Job and updates
これは、すべてが機能すると想定した場合の理想的なシナリオです。もちろん、実際の生活は失敗に満ちています。たとえば、最悪のケースの1つは、ネットワークが原因で#6
が失敗した場合です。ジョブは正しく実行されましたが、A
はそれについて何も認識していません。
このシステムのエラーを管理する方法についての軽量なアプローチを探しています。 たくさんのコンポーネントがあるので、エラー処理のためにそれらをすべてクラスター化しても意味がありません。次に、同じ理由で各コンポーネントに再度インストールされる分散メモリ/リポジトリの使用を取りやめました。
私の考えは、Bに1つのabsolute状態を持ち、A
に永続的な状態を決して持たないという方向に向かっています。これは、次のことを意味します。
#1
の前に、ワークユニット、つまりchangeが始まろうとしていることをA
にマークしますB
のみがこの状態のマークを解除できます。A
は、いつでもB
に関する情報を取得して、状態を更新できます。A
で同じユニットの新しいchangeを呼び出すことはできません。どう思いますか?この種のシステムのエラーを緩和する軽量の方法はありますか?
Aの永続的なログに追加するだけで十分です。これは、再起動やネットワークパーティションに対処して、最終的な一貫性を実現したり、破損を通知してそのような収束を防ぎます。償却 グループコミット を使用すると、ログエントリを永続化するための書き込みが1回未満で済みます。
あなたは、Bにマークのない状態の責任を負わせるよう提案しました。同意しません。 Aだけが新しい作業を認識し、Aだけがそれを追跡し、タイムアウトなどのエラーを報告する必要があります。 Bはべき等メッセージをAに送信し、Aは状態を更新し、必要に応じて間隔を置いて再クエリします。
ステップ0で、Aは新しい要求を認識してログに記録します。これは、Aが後で期限までに実行しなければならない義務を構成します。Aは、要求の処理が完了したことをAが知るまで、後続のステップを継続的に実行し、繰り返します。
一部のリクエストは他のリクエストよりも長くなります。処理時間の見積もりはAとBで利用できるようになりますが、おそらく処理が進むにつれて修正されます。このような推定はAにフィードバックされる可能性があるため、誤検知タイムアウトが発生することはめったにありません。 「まだ機能している、まだ機能している」というキープアライブメッセージと考えてください。
プッシュ戦略の代わりにプルを採用します。各パーツに他のパーツから変更をプルさせ、独自のレコードを更新します。
(私はWordキューを使用していますが、ログやトピックを代用することもできます。)
キューをサービスにベイクするか、個別のメッセージブローカーを用意できます。サービスに組み込まれた実装は、GET /jobrequests?from=<timestamp>
(Bが最後に処理されたジョブリクエストのタイムスタンプを追跡する)と同じくらい簡単です。
このようなアーキテクチャのトリッキーな部分は、少なくとも1回のセマンティクスと最低1回のセマンティクスを決定することです。具体的には、Bがキューからアイテムをプルし、実行中にクラッシュした場合、どうなりますか? 2つの可能性があり、どちらが最適かは、ユースケースによって異なります。
このアプローチの利点: