私たちは新しいWebベースの産業用アプリケーションを構築しています。ここ数日間頭を悩ませている質問の1つは、このアーキテクチャ上の異なる「マイクロサービス」間の統合についてです。
実際のマイクロサービスを定義するための概念を完全に採用しているわけではないので、私は少しの塩でマイクロサービスを使用しています。 1つ(そして私が最大だと思う)の違いは、異なるモジュール(「マイクロサービス」と呼んでいます)に沿って同じ共有データベースを使用しているという事実に依存しています。私たちのシステムの一種の論理的なビューは次のように描くことができます:
╔══════════════╗
║ Client ║ ══╗
╚══════════════╝ ║ (2)
║
▼
╔══════════════╗ (1) ╔══════════════╗
║ Serv. Reg. ║ <==> ║ API Gatew. ║
╚══════════════╝ ╚══════════════╝
█ █ █████████████ (4)
█ █ ████
╔══════════════╗ ╔══════════════╗ ╔══════════════╗
║ Module A ║ ║ Module B ║ ║ Module C ║ <===== "Microservices"
╚══════════════╝ ╚══════════════╝ ╚══════════════╝
║║ (3) ║║ (3) ║║ (3)
║║ ║║ ║║
╔══════════════════════════════════════════════════╗
║ Database Server ║
╚══════════════════════════════════════════════════╝
私たちがすでに理解しているいくつかのこと:
私たち(または私自身)が行き詰まっている点は、異なるモジュール間の通信を行う方法についてです。私はこれを行うためにさまざまなパターンとアンチパターンをたくさん読みましたが、ほとんどすべての人がRestTemplateまたはFeignやRibbonのような特別なクライアントを介したAPI統合を教えてくれます。
いくつかの理由、主にHTTPリクエストの同期的でステートレスな性質のため、私はこのアプローチを嫌う傾向があります。さまざまなモジュールのサービスレイヤーに強力なバインディングがあるため、HTTPのステートレスな性質が私の最大の問題です。たとえば、モジュールAで発生するアクションは、モジュールBとCに影響を与える可能性があり、すべてを「トランザクション」の観点から調整する必要があります。 HTTPがこれを制御する最良の方法だとは本当に思っていません。
Java私の内部のEEの部分は、EJBやRMIなどのサービス統合、または最終的にHTTPを使用しないもののいずれかを使用するように叫びます。私にとっては、はるかに「自然」ですモジュールA内のモジュールBから特定のサービスを配線し、それらがトランザクションに一緒に参加するようにします。
強調する必要があるもう1つのことは、データベースでの最終的な不整合などのパラダイムは、クライアントが深刻な種類のデータを扱っているため、クライアントにとって十分ではないということです。 SO、「私はデータで最善を尽くすことを約束します」はここではあまりうまく適合しません。
質問の時間:
「マイクロサービス」を扱う場合、これは本当に「サービス統合」ですか?それとも「リソース統合」が勝ちますか?
たとえば、Springは、EJBのような技術と同様に、サービス間のメッセージングを可能にするSpring Integrationを提供しているようです。これはそれらのサービスを統合する最良の方法ですか?何か不足していますか?
PS:私の「マイクロサービス」を「マイクロリス」と呼ぶかもしれません。 :)
マイクロサービスアーキテクチャはスケーラビリティと分散システム向けに設計されています。あなたは彼らが互いに話したり、同じデータベースを使用したりしたくありません。
どちらの場合も、分散型ステートレスアプローチを採用します。または、サービスをマージして1つのマクロサービスを作成します。コードをサービスクラスなどに分離することはできますが、トランザクションとサービス間通信をより適切に管理できます。
または、いくつかのデータベーステーブルを使用して手動トランザクションを実装することもできます。しかし、それはハックな解決策になるでしょう。
私の考えでは、「マイクロサービス」でここで使用されているパターンは、達成しようとしていることには適用できません。ただし、次の最良の方法は、各ステップを可能な限りステートレスで相互に独立させることです。
それらはすべて同じデータベースを共有している可能性がありますが、これらのマイクロサービスのそれぞれが独自のデータベース構成と接続プールを持つように作成します。処理する行は数値IDを使用して識別可能である必要があり、このIDはリクエスト内の各サービスに渡され、後続の各ステップで処理されます。
エラーが発生したときに最後に成功したステップがどこで実行されたかわからない問題を回避するには、このIDに関連付けられたレコードのステータスを更新する必要があります途中のすべてのステップ。したがって、レコード#347 しないへの変更を実行するリクエストが、その段階で想定する状態になっている場合、サービスは不正なリクエストステータスを返し、それ以上は実行されません。
トランザクションをエミュレートするには、IDに関連付けられたレコードを作成し、そのレコードが適切に処理されるように、そのIDで各マイクロサービスを呼び出す必要があります。トランザクションのように、エラーが発生したときに操作を「ロールバック」する必要があります。各マイクロサービスにもロールバックオプションが必要です。最後に成功したステップの状態を指定して、各マイクロサービスへの呼び出しを実行しますin逆順このロールバックオプションを使用すると、完全に前の状態に戻ります。このロールバック操作で他に何も実行されない場合でも、以前の状態を復元する必要があります。
要約すると:
このように、トランザクションのマルチステップ処理を実行する手段を提供しながら、マイクロトランザクションは可能な限りステートレスなままです。
私はニールに同意します。完全なイベントの振り付けが必要だったので、ニールは上記のアプローチを採用しましたが、結果の一貫性が排除され、トランザクションの原子性が必要なため、佐賀または2PCのアプローチを検討できます。考慮すべきもう1つのポイントは、ワークフローごとに、いくつのマイクロサービス/モジュールとしてワークフローを操作するかです。それがかなり低い場合(5未満が安全な仮定です)、佐賀のパターンを捨てることができます。それ以外の場合は、佐賀を検討することをお勧めします。