現在のアーキテクチャをマイクロサービスに移行することを検討するように依頼されました。リクエストが失敗する可能性があると常に想定しているという警告を認識しています。
したがって、リクエストを再試行する準備を常に整えておく必要があることを承知しています。ただし、これを設計するときに、再試行が失敗する可能性があることも想定しています。
そのことを念頭に置いて、すべての処理がコミットされるか常にロールバックされるパターンを見てきました。これは、メッセージの送信トレイ(および受信トレイ) 送信トレイパターン によって実現されます。サービスは機能の変更をデータベースに保存し、同じトランザクション内でイベントメッセージをデータベースの送信トレイに保存します。次に、別個のディスパッチャーサービスが送信トレイからメッセージをディスパッチし、メッセージングシステムに送信します。詳細は、この一連の記事で詳しく説明しています 分散トランザクションを超えた生涯:背教者の実装
ディスパッチャーがメッセージの送信に失敗した場合、再試行できるので、これは私にとって最も安全なオプションです。
しかし、私の同僚の1人は、再試行する必要はありますが、メッセージが常にメッセージングシステムに正常に送信されるように、ソリューションには十分な回復力があると考えています。例えば。再試行の必要性を引き起こす問題は常に一時的なものであり、再試行の1つが成功するまでに時間内に解決されます。
私は慎重すぎて再試行で十分かどうかについての意見を探しています。したがって、ディスパッチャーや送信トレイのパターンは必要ありません。
主な問題は、私が呼び出しているサービスにアクセスできないことではなく、サービスが実行されているサーバーがシャットダウンすることです。
送信トレイと再試行の戦略は機能しますが、各マイクロサービスに多くの複雑さを追加します。この戦略は必然的に非同期のアプローチを強制するため、イベント駆動型アーキテクチャの使用を検討してください。
マイクロサービスは、高可用性イベントチャネル(kafka、rabbitmqなど)で公開されたイベントを介して通信します。各サービスはチャネルからイベントをプルし、それらが応答する必要があることを何でも行い、結果として新しいイベントを生成します。
このアプローチの利点:
欠点は、イベントソーシングスタイルですべてを再設計する必要があり(困難な場合があります)、イベントの急増を管理するのが難しい場合があることです(ソリューション:スキーマストア、AsyncAPI/Cloudevents.io、相関および因果関係ID、すべてのイベントを監視して記録するロギングサービス)。
法学者が指摘したように、分散コンピューティングの最初の誤りは "ネットワークは信頼できる。" です。
提案された送信トレイソリューションは、送信メッセージが常に正常に配信されることを保証するのに最適です。障害の原因が存在する期間を事前に知る方法はありません。 1回リトライしますか? 5回?試行の間に1秒間待機しますか? 30秒? 15分?
回復時間は、次の再試行で成功する一時的なエラーから、サービスの復元に数日かかる物理的な災害のようなさらに極端なものまで、どこでもかまいません。最も信頼性の高いネットワークやインフラストラクチャでも、定期的に問題が発生しています。
失敗した更新からも障害が発生する可能性があります。私の会社では、サービスの内部依存関係と外部依存関係の両方の更新を展開した後、特定のメッセージハンドラーが完全に失敗し始めるケースがありました。システム内の新しいメッセージは、失敗するまで可能な限り続きました。この場合、原因を修正するのに人間の介入が必要でしたが、修正を実装するのに数分から数日かかることもあります。はい、私達はテストしますが、物事は時々まだ亀裂をすり抜けます。
回復可能性への一般的なアプローチは複数の段階です
即時-これは、遅延がないか、ほとんど遅延せずに、失敗したメッセージをすぐに再試行することにより、一時的なエラーを処理します。
遅延-これは、再試行の間隔が長くなり、即時の再試行が成功しなかったシナリオをカバーします。最初の再試行に10秒、1秒に20秒などで開始する場合があります。停止が長く続くほど、受信側システムに回復する時間を与えます。たとえば、過負荷のWebサーバーは回復する可能性が高くなります。長く待つほど。
手動-最終的に系統的な再試行は失敗し、失敗したメッセージを送信トレイ/メッセージングシステムから別の場所に移動して、システム管理者(操作、お気に入りのタイトルを選択)が別の場所に移動し、手動での介入が必要なエラーを確認して再試行します。いくつかの遅延した再試行の後、メッセージはここに移動されます。これにより、手動による介入が行われるまで成功する可能性が低いメッセージで送信トレイまたはキューシステムをブロックすることがなくなり、他のメッセージが妨げられずに流れ続けることができます。
分散型のメッセージベースのシステムで実行できる最悪のことは、メッセージを失うことです。そのため、後で続行する方法がない限り、再試行をあきらめないでください。
この情報は以下に基づいています。
リクエストは成功したがレスポンスが失敗した場合、クライアントはリクエストが失敗したと見なし、サーバーは成功したと見なすため、問題があることに注意してください。
あなたが探している言葉は べき等 です。
ここにも役立つ情報: 分散コンピューティングの誤り -読んで理解し、それに基づいてシステムを設計してください!
それはあなたがどれだけの回復力を必要とするかによると思います。受信者が常に一定の再試行回数以内に準備ができていると想定することはできません。
送信トレイパターンは確かに実行可能なソリューションです(おそらく最良のソリューションです)が、何らかのログメカニズムがあれば十分なので、失敗したメッセージを追跡できますか?
結局、それはあなたのニーズに依存しますが、私の知識では、送信トレイパターンも私の頼りになると思います。
信頼性のために設計されたいくつかのパターンがあります。
大規模なデプロイの場合、1つ以上のマイクロサービスで複数のインスタンスが同時に実行されるのが一般的です。これは、再試行が通常の操作でのサービス品質(QoS)ニーズの大部分に役立つことを意味します。通常、既存の要求の終了と再試行に関連するタイムアウトがあります。
マイクロサービスがスタックして、接続が適切に終了しない場合があります。これらの場合、回路ブレーカーは要求と再試行ロジックを遮断し、デフォルトの応答を返します(エラーコードの場合があります)。回路ブレーカーは、カスケード障害を防ぐように設計されています。
2つのパターンは 相互に排他的ではありません です。
デプロイメントが失敗した場合...
これらのアーキテクチャパターンと組み合わせると、 DevOps (および DevSecOps )の概念になります。ここでの基本原則は、継続的な展開の1つです。物事がひどくうまくいかないことがあり、そこから回復する必要があります。 DevOpsは、次のことを確実にすることでそれに対処しています。
すべてのチームが最高の能力を発揮するのと同じように、システム全体がまだ準備が整っていない何かが本番環境に移行することがあります。ユーザーへの影響が完全に受け入れられなくなるまで、本番環境を既知の安定したビルドに戻すための限られた時間があります。
デプロイメントシステムが最後のバージョンを認識している場合は、単にそのバージョンを再度デプロイし、テストネットワークでより詳細なテストとトリアージを行うだけです。ねじれが解消されたら、修正バージョンを再デプロイできます。