web-dev-qa-db-ja.com

マイクロサービスの最も受け入れられているトランザクション戦略は何ですか

マイクロサービスを備えたシステムで発生する主な問題の1つは、トランザクションが異なるサービスにまたがる場合のトランザクションの動作です。私たち自身のアーキテクチャでは、これを解決するために分散トランザクションを使用していますが、それらには独自の問題があります。特にデッドロックはこれまでのところ苦痛でした。

別のオプションは、システム内のフローを認識し、システム全体にまたがるバックグラウンドプロセスとしてロールバックを処理する、カスタムメイドのトランザクションマネージャーのようなものです(したがって、他のサービスにロールバックを指示します)ダウンしている場合は、後で通知します)。

受け入れられる別のオプションはありますか?これらの両方に欠点があるようです。 1つ目はデッドロックやその他の問題を引き起こす可能性があり、2つ目はデータの不整合を引き起こす可能性があります。より良いオプションはありますか?

88
Kristof

通常のアプローチは、それらのマイクロサービスを可能な限り分離することです-それらを単一のユニットとして扱います。その後、トランザクション全体をサービスのコンテキストで開発できます(つまり、通常のDBトランザクションの一部ではありませんが、サービスの内部にDBトランザクションを保持することはできます)。

トランザクションがどのように発生し、サービスにとってどのような意味があるかを考えてください。元の操作を元に戻すロールバックメカニズム、または実際にコミットするように指示されるまで元の操作を予約する2フェーズコミットシステムを実装できます。もちろん、これらのシステムはどちらも独自のシステムを実装していることを意味しますが、すでにマイクロサービスを実装しています。

金融サービスは常にこの種のことを行います-私の銀行からあなたの銀行にお金を移動したい場合、DBのような単一のトランザクションはありません。どちらかの銀行が実行しているシステムがわからないため、それぞれをマイクロサービスのように効果的に扱う必要があります。この場合、私の銀行は私のお金を私の口座から保留口座に移動し、銀行にいくらかのお金があることを伝えます。その送金が失敗した場合、私の銀行は送金しようとしたお金で私の口座を払い戻します。

43
gbjbaanb

標準的な知識は、トランザクションがマイクロサービスの境界を越えないようにすることです。与えられたデータのセットが実際に別のものとアトミックに一貫している必要がある場合、それら2つは一緒に属します。

これは、完全に設計するまでシステムをサービスに分割することが非常に難しい理由の1つです。現代の世界では、これはおそらくそれを書くことを意味します...

31
soru

一貫性がアプリケーションの強力な要件である場合、マイクロサービスがより良いアプローチであるかどうかを自問する必要があると思います。マーティンファウラーのように says

マイクロサービスは、分散型データ管理への説得力のある主張のため、結果整合性の問題を引き起こします。モノリスを使用すると、一連のことを1つのトランザクションでまとめて更新できます。マイクロサービスは更新するために複数のリソースを必要とし、分散トランザクションは(正当な理由により)嫌われます。したがって、今、開発者は一貫性の問題を認識し、コードが後悔することを行う前に、同期が取れていないことを検出する方法を理解する必要があります。

しかし、おそらくあなたの場合、可用性の位置で一貫性を犠牲にすることができます

多くの場合、ビジネスは可用性を重視するため、ビジネスプロセスは、多くの場合、思ったよりも不整合を許容します。

ただし、マイクロサービスでの分散トランザクションの戦略があるかどうかも自問していますが、コストが高すぎる可能性があります。マーティンファウラーの常に優れた記事と [〜#〜] cap [〜#〜] の定理を使って2セントを差し上げたかったのです。

17
gabrielgiussi

ここでの回答の少なくとも1つだけでなく、Webの他の場所でも提案されているように、2つのエンティティ間の整合性が必要な場合は、通常のトランザクション内でエンティティを永続化する1つのマイクロサービスを設計できます。

しかし同時に、エンティティが実際には同じマイクロサービスに属していない状況も考えられます。たとえば、販売レコードと注文レコード(販売を遂行するために何かを注文したとき)などです。このような場合、2つのマイクロサービス間の整合性を確保する方法が必要になる場合があります。

従来の分散トランザクションが使用されてきましたが、私の経験では、ロックが問題になるサイズにスケーリングするまで、トランザクションはうまく機能します。ロックを緩和して、関連するリソース(たとえば、販売されているアイテム)だけが状態変更を使用して「ロック」されるようにすることができますが、すべてのアプリケーションを構築する必要がある領域に入るので、ここがトリッキーになります。データベースで処理するのではなく、自分でこれを行うロジック。

私は、この複雑な問題を処理するための独自のトランザクションフレームワークを構築する道を進んでいる企業と協力してきましたが、コストがかかり、成熟するのに時間がかかるため、お勧めしません。

システムにボルトで固定できる製品があり、一貫性を保っています。ビジネスプロセスエンジンは良い例であり、通常は一貫性を最終的に処理し、補正を使用します。他の製品も同様に機能します。 通常、クライアントの近くにソフトウェアのレイヤーができ、一貫性とトランザクションを処理し、(マイクロ)サービスを呼び出して実際のビジネス処理を行います。そのような製品の1つは、Java EEソリューションで使用できる汎用JCAコネクターです(透明性のために:私が作者です)。参照 http://blog.maxant.co .uk/pebble/2015/08/04/1438716480000.html 詳細とここで提起された問題のより深い議論については.

トランザクションと一貫性を処理する別の方法は、マイクロサービスへの呼び出しをメッセージキューのようなトランザクションへの呼び出しにラップすることです。上記の販売レコード/注文レコードの例を見てみましょう。販売マイクロサービスに注文システムにメッセージを送信させるだけで、データベースに販売を書き込むのと同じトランザクションでコミットされます。結果は、veryを適切にスケーリングする非同期ソリューションです。 Webソケットなどのテクノロジーを使用すると、非同期ソリューションのスケールアップに関連することが多いブロッキングの問題を回避することもできます。このようなパターンに関する他のアイデアについては、私の別の記事を参照してください: http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.html

どちらのソリューションを選択したとしても、システムのごく一部のみが一貫性を必要とするものを書き込んでいることを認識することが重要です。ほとんどのアクセスは読み取り専用である可能性があります。そのため、トランザクション管理をシステムの関連部分のみに組み込んで、システムを適切にスケーリングできるようにします。

16
Ant Kutschera

私が慣れている以上に妥協する多くの解決策があります。確かに、異なる銀行間でお金を移動するなど、ユースケースが複雑な場合、より快適な代替手段は不可能かもしれません。ただし、マイクロサービスの使用がデータベーストランザクションになる可能性があるという一般的なシナリオで何ができるかを見てみましょう。

オプション1:可能な場合はトランザクションの必要性を回避します

明白で前に述べましたが、それを管理できれば理想的です。コンポーネントは実際には同じマイクロサービスに属していましたか?または、トランザクションが不要になるようにシステムを再設計できますか?おそらく、非トランザクション性を受け入れることが最も手頃な犠牲になります。

オプション2:キューを使用する

他のサービスが実行したいすべてのことで他のサービスが成功するのに十分な確実性がある場合は、なんらかのキューを介して呼び出すことができます。キューに入れられたアイテムは後で取得されますが、アイテムがキューに入れられていることを確認できます

たとえば、エンティティを挿入して電子メールを1つのトランザクションとして送信するとします。メールサーバーを呼び出す代わりに、テーブルの電子メールをキューに入れます。

Begin transaction
Insert entity
Insert e-mail
Commit transaction

明らかな欠点は、複数のマイクロサービスが同じテーブルにアクセスする必要があることです。

オプション3:トランザクションを完了する直前に、外部の作業を最後に行います

このアプローチは、トランザクションのコミットが失敗する可能性が非常に低いという想定に基づいています。

Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction

クエリが失敗した場合、外部呼び出しはまだ行われていません。外部呼び出しが失敗した場合、トランザクションはコミットされません。

このアプローチには、one外部呼び出ししかできないという制限があり、最後に実行する必要があります(つまり、クエリでその結果を使用することはできません)。

オプション4:保留状態で物事を作成します

投稿されたように here を使用すると、複数のマイクロサービスで異なるコンポーネントを作成し、それぞれを非トランザクションで保留状態にすることができます。

検証が実行されますが、最終的な状態では何も作成されません。すべてが正常に作成された後、各コンポーネントがアクティブ化されます。通常、この操作は非常に単純で、問題が発生する可能性は非常に小さいため、非トランザクションでアクティブ化することもできます。

最大の欠点は、おそらく保留中のアイテムの存在を考慮しなければならないことです。選択クエリでは、保留中のデータを含めるかどうかを検討する必要があります。ほとんどはそれを無視するべきです。そして、更新はまったく別の話です。

オプション5:マイクロサービスにクエリを共有させます

他のどのオプションもあなたのためにそれをしませんか?次に、unorthodoxを取得しましょう。

会社によっては、これは受け入れられない場合があります。私は承知しています。これは正統ではありません。それが受け入れられない場合は、別のルートに進んでください。しかし、これがあなたの状況に合えば、問題を簡単かつ強力に解決します。これは、最も許容できる妥協策である可能性があります。

複数のマイクロサービスからのクエリを単純な単一のデータベーストランザクションに変換する方法があります。

実行するのではなく、クエリを返します。

Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction

ネットワークに関しては、各マイクロサービスが各データベースにアクセスできる必要があります。将来のスケーリングについても、このことを覚えておいてください。

トランザクションに関与するデータベースが同じサーバー上にある場合、これは通常のトランザクションになります。それらが異なるサーバー上にある場合、それは分散トランザクションになります。コードは関係なく同じです。

接続タイプ、パラメータ、接続文字列を含むクエリを受け取ります。それをきちんと実行可能なコマンドクラスにラップして、フローを読みやすくします。マイクロサービスの呼び出しにより、トランザクションの一部として実行されるコマンドが生成されます。

接続文字列は、元のマイクロサービスが提供するものであるため、すべての意図と目的において、クエリはそのマイクロサービスによって実行されると見なされます。クライアントマイクロサービスを介して物理的にルーティングしているだけです。違いはありますか?まあ、それは別のクエリと同じトランザクションに置くことができます。

妥協が受け入れられる場合、このアプローチにより、マイクロサービスアーキテクチャにおけるモノリスアプリケーションの単純なトランザクション性が得られます。

2
Timo

マイクロサービスでは、diff間の一貫性を実現する3つの方法があります。サービス:

  1. オーケストレーション-サービス全体でトランザクションとロールバックを管理する1つのプロセス。

  2. コレオグラフィー-互いにサービスパスメッセージをやり取りし、最終的に一貫した状態に到達します。

  3. ハイブリッド-上記の2つを組み合わせる。

詳細については、リンクにアクセスしてください: https://medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c

1
techagrammer

問題空間の分解 サービス境界の識別 から始めます。適切に行われると、サービス間でトランザクションを行う必要がなくなります。

さまざまなサービスには、独自のデータ、動作、動機、政府、ビジネスルールなどがあります。まず、企業が持っている高レベルの機能をリストすることから始めます。たとえば、マーケティング、販売、経理、サポート。もう1つの出発点は組織構造ですが、注意点があることに注意してください-いくつかの理由(たとえば、政治的)のため、最適なビジネス分解スキームではない可能性があります。より厳密なアプローチは Value chain analysis です。覚えておいてください、あなたのサービスは人々も含むことができます、それは厳密にソフトウェアではありません。サービスは events を介して相互に通信する必要があります。

次のステップは、これらのサービスを分割することです。その結果、比較的独立した aggregates が得られます。それらは一貫性の単位を表します。言い換えると、それらの内部は一貫していて、ACIDである必要があります。集合体は、イベントを介して相互に通信します。

ドメインが最初に一貫性を要求すると思う場合は、もう一度考えてください。これを念頭に置いて構築された大きなミッションクリティカルなシステムはありません。それらはすべて分散されており、最終的には一貫しています。 Pat Hellandの クラシックペーパー を確認してください。

ここ は、分散システムの構築方法に関する実用的なヒントです。

0
Zapadlo