今日は、マイクロサービスと分散アーキテクチャに関する小さな資料を紹介します。特に、新しいシステムはモノリスから開始する必要があるというMartin Fowlerの考えに触れており、開発されたマイクロサービスアーキテクチャでも、大きなモノリシックコアを残すことをお勧めします。
読書を楽しむ!
今日、誰もがマイクロサービスについて考え、書いていますが、私も例外ではありません。マイクロサービスの基本原則とその真のコンテキストに基づいて、マイクロサービスが分散システムであることは明らかです。
分散トランザクションとは何ですか?
ネットワーク上の複数の物理システムまたはコンピューターにまたがるトランザクションは、単に分散トランザクションと呼ばれます。マイクロサービスの世界では、トランザクションは、トランザクション全体を完了するために順番に呼び出される複数のサービスに分割されます。
これは、トランザクションを使用するモノリシックオンラインストアシステムです
。 1:モノリスでのトランザクション
上記のシステムで、ユーザーが注文リクエスト(チェックアウト)をプラットフォームに送信すると、プラットフォームはデータベースにローカルトランザクションを作成し、このトランザクションは多くのデータベーステーブルにまたがって注文を処理(処理)し、予約します(予約)倉庫からの商品。これらの手順のいずれかが失敗した場合、トランザクションをロールバックできます。これは、注文自体と予約済み商品の両方の拒否を意味します。この一連の原則はACID(Atomicity、Consistency、Isolation、Durability)と呼ばれ、データベースシステムレベルで保証されています。
マイクロサービスから構築されたオンラインストアシステムの分解を次に示します。
図2:マイクロサービスでのトランザクション
このシステムを分解した後、マイクロサービスを作成し
OrderMicroservice
、InventoryMicroservice
別々のデータベースで。ユーザーからチェックアウト要求が来ると、これらのマイクロサービスの両方が呼び出され、それぞれがデータベースに変更を加えます。トランザクションは現在、複数のシステムにまたがる複数のデータベースに分散しているため、分散していると見なされます。
マイクロサービスで分散トランザクションを実行する場合の問題は何ですか?
マイクロサービスアーキテクチャの導入により、データベースはACIDの性質を失っています。多くのマイクロサービス、つまりデータベース間でトランザクションが急増する可能性があるため、次の重要な問題に対処する必要があります。
トランザクションの原子性を維持する方法は?
アトミシティとは、どのトランザクションでも、すべてのステップを完了できるか、完了できないことを意味します。上記の例がメソッドの「アイテムの注文」操作を完了できない場合、
InventoryMicroservice
適用された「注文処理」の変更をロールバックするにはどうすればよいOrderMicroservice
ですか?
競合するリクエストを処理するにはどうすればよいですか?
いずれかのマイクロサービスからのオブジェクトが長期保存のためにデータベースに入り、同時に別の要求が同じオブジェクトを読み取るとします。サービスはどのデータを返す必要がありますか?古いか新しいですか?上記の例で
OrderMicroservice
は、すでに作業が完了してInventoryMicroservice
更新中である場合、ユーザーによる注文のリクエスト数に現在の注文を含める必要がありますか?
最新のシステムは潜在的な障害を念頭に置いて設計されており、分散トランザクション処理の主要な問題の1つは、PatHellandによって明確に説明されています。
原則として、開発者は、分散トランザクションの操作を伴うような大規模でスケーラブルなアプリケーションを作成しません。
可能な解決策
上記の2つの問題は、マイクロサービスベースのアプリケーションを設計および構築する上で非常に重要です。それらを解決するために、次の2つのアプローチが使用されます。
- 二相固定
- 究極の一貫性と補償/ SAGA
1.二相固定
名前が示すように、トランザクションを処理するこの方法には、準備フェーズとコミットフェーズの2つのステージが含まれます。この場合の重要な役割は、トランザクションのライフサイクルを編成するトランザクションコーディネーターによって果たされます。
使い方
準備段階で、作業に参加しているすべてのマイクロサービスはコミットの準備をし、トランザクションを完了する準備ができていることをコーディネーターに通知します。次に、次のステップで、コミットが発生するか、トランザクションコーディネーターがすべてのマイクロサービスにコマンドを発行してロールバックします。
例としてオンラインストアシステムをもう一度考えてみましょう。
図3:マイクロサービスシステムでの2フェーズコミットの成功
上記の例(図3)では、ユーザーが注文リクエストを送信すると、コーディネーターは
TransactionCoordinator
最初に完全なコンテキスト情報を使用してグローバルトランザクションを開始します。まず、prepareコマンドをマイクロサービスOrderMicroservice
に送信して注文を作成します。次に、準備コマンドをに送信しますInventoryMicroservice
アイテムを予約します。両方のサービスが変更を加える準備ができると、オブジェクトがそれ以上変更されないようにブロックし、それについて通知しますTransactionCoordinator
。TransactionCoordinator
すべてのマイクロサービスが変更を適用する準備ができていることを確認すると、トランザクションのコミットを要求することにより、これらのマイクロサービスにそれらを保存するように命令します。この時点で、すべてのオブジェクトのロックが解除されます。
図4:マイクロサービスでの作業中に失敗した2フェーズコミット
失敗シナリオ(図4)-いずれかの時点で単一のマイクロサービスに準備する時間がない場合は
TransactionCoordinator
、トランザクションをキャンセルしてロールバックプロセスを開始します。図ではOrderMicroservice
、何らかの理由で注文を作成できませんでしたが、注文InventoryMicroservice
を作成する準備ができていると回答しました。コーディネーターTransactionCoordinator
はでキャンセルを要求しますInventoryMicroservice
、その後、サービスは行われたすべての変更をロールバックし、データベースオブジェクトのロックを解除します。
利点
- このアプローチは、トランザクションの原子性を保証します。トランザクションは、両方のマイクロサービスが成功したとき、またはマイクロサービスが変更を加えなかったときに完了します。
- 次に、トランザクションコーディネーターがこれらの変更をコミットするまでオブジェクトへの変更は表示されないため、このアプローチでは読み取りと書き込みを分離できます。
- このアプローチは、クライアントに成功または失敗が通知される同期呼び出しです。
短所
- 何も完璧ではありません; 2フェーズのコミットは、単一のマイクロサービス操作に比べて非常に低速です。彼らはコーディネーターに大きく依存しています。トランザクション。高負荷の期間中にシステムの速度を大幅に低下させる可能性があります。
- もう1つの大きな欠点は、データベース行のロックです。ロックはパフォーマンスのボトルネックになる可能性があり、2つのトランザクションが互いに緊密にロックするデッドロックが発生する可能性があります。
2.究極の一貫性と補償/ SAGA
一貫性の最良の定義の1つは、最終的にmicroservices.ioで提供されます。各サービスは、データが更新されるたびにイベントを公開します。他のサービスはイベントを購読します。イベントを受信すると、サービスはそのデータを更新します。
このアプローチでは、分散トランザクションは、対応するマイクロサービス上の非同期ローカルトランザクションのコレクションとして実行されます。マイクロサービスは、イベントバスを介して情報を交換します。
使い方
繰り返しになりますが、オンラインストアで実行されているシステムの例を見てみましょう。
図5:究極の一貫性/ SAGA、成功
上記の例(図5)では、顧客はシステムに注文を処理するように要求します。このリクエスト
Choreographer
により、Create Orderイベントが発生し、トランザクションが開始されます。マイクロサービスOrderMicroservice
はこのイベントをリッスンして注文を作成します。この操作が成功すると、OrderCreatedイベントが発生します。コーディネーターChoreographer
はこのイベントをリッスンし、アイテムの注文に進み、アイテムの予約イベントを発生させます。マイクロサービスInventoryMicroservice
このイベントを聞いて商品を注文します。このイベントが成功すると、ItemsReservedイベントが発生します。この例では、これはトランザクションが終了したことを意味します。
マイクロサービス間のすべてのイベントベースの通信はイベントバスを介して行われ、別のシステムがその編成(コレオグラフィー)を担当します。これにより、不必要な複雑さで問題を解決できます。
図6:究極の一貫性/ SAGA、失敗した結果
何らかの理由で
InventoryMicroservice
アイテムが予約されなかった場合(図6)、アイテムの予約に失敗したイベントが発生します。コーディネーターChoreographer
はこのイベントをリッスンし、オフセットトランザクションを開始して、DeleteOrderイベントを発生させます。マイクロサービスOrderMicroservice
このイベントをリッスンし、以前に作成された注文を削除します。
利点
このアプローチの主な利点は、各マイクロサービスが独自のアトミックトランザクションのみに焦点を当てていることです。別のサービスの実行に比較的長い時間がかかる場合、マイクロサービスはブロックされません。これは、データベースをロックする必要もないことも意味します。提案されたソリューションは非同期であり、イベントの処理に基づいているため、このアプローチを使用すると、高負荷で作業するときにシステムの優れたスケーラビリティを確保できます。
短所
このアプローチの主な欠点は、読み取りの分離が提供されないことです。したがって、上記の例では、注文が作成されたことが顧客に表示されますが、1秒後に、相殺トランザクション中に注文が削除されます。さらに、マイクロサービスの数が増えると、それらのデバッグと保守がより困難になります。
結論
提案されたアプローチの最初の代替案は、分散トランザクションを完全に放棄することです。新しいアプリケーションを構築する場合は、Martin FowlerによるMonolithFirstで説明されているように、モノリシックアーキテクチャから始めます。彼を引用します。
, , . , , . —単一のイベントの結果として一度に2つの場所でデータを更新する必要がある場合は、2フェーズアプローチよりも分散トランザクションを処理するために、最終的に一貫性/ SAGAアプローチが推奨されます。主な理由は、分散環境での2フェーズアプローチが拡張できないことです。一貫性を使用すると、データベースをアトミックに更新してイベントを発生させる方法など、最終的には独自の問題が発生します。このような開発哲学に移ると、開発者の視点とテスターの視点の両方からその認識を変える必要があります。