私は、大規模でタスク集約型のドキュメント処理システムであなたが説明したものと非常によく似たシステムを構築しましたが、過去 7 年間、長所と短所の両方を受け入れなければなりませんでした。あなたのアプローチは堅実で実行可能ですが、いくつかの欠点があります。
状態の変化に対して脆弱である可能性があります (つまり、すべてのステップがキューに入れられる前にプロセスの入力が変更された場合、後のステップの入力が前のステップと矛盾する可能性があります)。
DB とキューの両方を含む必要以上のインフラストラクチャ = 障害点が多く、セットアップが難しく、より多くのドキュメントが必要 = あまり正しくない
複数のワーカーが同じステップで同時に動作しないようにするにはどうすればよいですか? 言い換えれば、DB 行は 4 つのステップが完了したことを示していますが、ワーカー プロセスは #5 を実行できるかどうかをどのように判断するのでしょうか? 別のプロセスがすでにこれに取り組んでいるかどうかを知る必要はありませんか? いずれにせよ (DB または MQ)、ロックのために追加の状態を含める必要があります。
あなたの例は失敗に対して堅牢ですが、同時実行性には対応していません。並行性に対処するために状態を追加すると、障害処理が深刻な問題になります。たとえば、プロセスはステップ 5 を実行してから、DB 行を「作業中」状態にします。その後、そのプロセスが失敗すると、ステップ 5 は「作業中」状態のままになります。
あなたのオーケストレーターは、多くの同期DB操作を行っているため、少し重いです.1つしか存在しない可能性があるため、アーキテクチャの残りの部分と同様に拡張できないのではないかと心配しています...これは依存しますステップの実行時間がデータベース トランザクションと比較してどのくらい長いかについて、これはおそらく非常に大規模な場合にのみ問題になるでしょう。
もう一度やり直さなければならない場合は、さらに多くのオーケストレーションをワーカー プロセスにプッシュすることは間違いありません。したがって、オーケストレーション コードは一般的であり、任意のワーカー プロセスから呼び出すことができますが、中央の制御プロセスはできるだけ軽量に保ちます。また、アーキテクチャをシンプルにして同期性を低く保つために、データベースは使用せずにメッセージ キューのみを使用します。
IN と WIP (進行中の作業) の 2 つのキューを持つ交換を作成します。
中央プロセスは、プロセス リクエストのサブスクライブと、タイムアウトしたステップの WIP キューのチェックを担当します。
1) 中央プロセスが特定の処理 (X) の要求を受け取ると、オーケストレーション コードを呼び出し、最初のタスク (X1) を IN キューにロードします。
2) 最初に使用可能なワーカー プロセス (P1) は、X1 をトランザクション的にデキューし、保守的な存続時間 (TTL) タイムアウト値で WIP キューにエンキューします。このデキューはアトミックであり、IN には他の X タスクがないため、X タスクで 2 番目のプロセスを実行することはできません。
3) P1 が突然終了した場合、タイムアウトを除いて、地球上のどのアーキテクチャもこのプロセスを保存できません。タイムアウト期間の終わりに、中央プロセスはタイムアウトになった X1 を WIP で見つけ、トランザクションで X1 を WIP からデキューし、IN にエンキューして、適切な通知を提供します。
4) P1 が正常に異常終了した場合、ワーカー プロセスはトランザクションで X1 を WIP からデキューし、IN にエンキューして、適切な通知を提供します。例外によっては、ワーカー プロセスが TTL をリセットしてステップを再試行することもできます。
5) P1 が無期限にハングするか、その TTL を超えると、#3 と同じ結果になります。中央プロセスがそれを処理し、おそらくワーカー プロセスはある時点でリサイクルされます。または、タイムアウトが発生するたびにワーカー プロセスをリサイクルするようにルールを設定することもできます。
6) P1 が成功すると、ワーカー プロセスは次のステップ (X2 または X-done) を決定します。次のステップが X2 の場合、ワーカー プロセスはトランザクションで X1 を WIP からデキューし、X2 を IN にエンキューします。次のステップが X-done の場合、処理は完了し、適切なアクションを実行できます。おそらくこれは、オーケストレーターによる後続の処理のために X-done を IN にエンキューすることです。
私が提案するアプローチの利点は次のとおりです。
ワーカー プロセス間の競合が指定されている
考えられるすべての障害シナリオ (クラッシュ、例外、ハング、および成功) が処理されます
シンプルなアーキテクチャは、RabbitMQ を使用してデータベースなしで完全に実装できるため、スケーラビリティが向上します
ワーカーが次のステップの決定とエンキューを処理するため、より軽量なオーケストレーターがあり、よりスケーラブルなシステムにつながります
唯一の本当の欠点は、状態の変化に対して潜在的に脆弱であることですが、多くの場合、これは懸念の原因ではありません。これがシステムの問題であるかどうかを知ることができるのは、あなただけです。
これに関する私の最終的な考えは次のとおりです。このオーケストレーションには十分な理由があるはずです。結局のところ、プロセス P1 がタスク X1 を終了し、あるプロセスが次のタスク X2 を処理するときが来た場合、X1 を終了したばかりで現在利用可能であるため、P1 は非常に良い候補になるようです。その論理によれば、プロセスは完了するまですべてのステップを実行する必要があります。タスクを連続して実行する必要がある場合、なぜプロセスを混ぜ合わせる必要があるのでしょうか。唯一の非同期境界は、実際にはクライアントとワーカー プロセスの間にあります。ただし、これを行う正当な理由があると仮定します。たとえば、プロセスは別のマシンやリソースに特化したマシンで実行できます。