web-dev-qa-db-ja.com

APIゲートウェイ(REST)+イベント駆動型マイクロサービス

APIゲートウェイパターンに従ってREST APIを介して公開する機能を持つマイクロサービスの束があります。これらのマイクロサービスはSpring Bootアプリケーションであるため、Spring AMQPを使用してRPCスタイルの同期通信を実現していますこれらのマイクロサービスの間で、順調に進んでいます。しかし、イベントドリブンマイクロサービスアーキテクチャについて読み、Spring Cloud Streamなどのプロジェクトを検討するほど、RPCで間違った方法を実行している可能性があると確信するようになります。 、同期アプローチ(特に、クライアントアプリケーションからの1秒あたり数百または数千のリクエストに応答するためにこれをスケーリングする必要があるため)。

イベント駆動型アーキテクチャの背後にあるポイントを理解しています。私がよく理解していないのは、すべての要求に対する応答を期待するモデル(REST)の後ろに座っているときに、このようなパターンを実際に使用する方法です。たとえば、APIゲートウェイをマイクロサービスとして使用し、ユーザーを保存および管理する別のマイクロサービスを使用している場合、GET /users/1純粋にイベント駆動の方法で?

16
Tony E. Stark

私の後に繰り返して:

RESTおよび非同期イベントは代替手段ではありません。これらは完全に直交しています。

どちらか、または両方、あるいは両方を持たせることができます。それらは、まったく異なる問題ドメインのためのまったく異なるツールです。実際、汎用の要求と応答の通信は、非同期で、イベント駆動型で、フォールトトレラントであることが絶対に可能です


簡単な例として、AMQPプロトコルはTCP接続を介してメッセージを送信します。 TCPでは、すべてのパケットが受信者によって確認される必要があります。パケットの送信者がそのパケットのACKを受信しない場合、ACKが送信されるまで、またはアプリケーション層が「放棄」して接続を放棄するまで、そのパケットを再送信し続けます。すべての "パケット送信要求" mustには付随する "パケット確認応答"があり、応答に失敗すると接続全体が失敗するため、これは明らかにフォールトトレラントでない要求応答モデルです。しかし、非同期フォールトトレラントメッセージングのために標準化され広く採用されているプロトコルであるAMQPは、TCPを介して通信されます。何ができますか?

ここで中心となる概念は、スケーラブルな疎結合のフォールトトレラントメッセージングが送信するメッセージではなく、送信方法によって定義されることです。つまり、疎結合はアプリケーション層で定義されますです。

RESTful HTTPと直接通信するか、AMQPメッセージブローカーと間接的に通信する2つのパーティを見てみましょう。パーティーAがパーティーBにJPEG画像をアップロードしたいとします。パーティーBは画像を鮮明化、圧縮、またはその他の方法で強化します。パーティーAは、処理された画像をすぐに必要としませんが、将来の使用と取得のために参照する必要があります。 RESTで使用できる方法の1つは次のとおりです。

  • パーティAがHTTP POST要求メッセージをパーティBにContent-Type: image/jpegで送信します。
  • パーティーAが待機している間、パーティーBは画像を処理します(画像が大きい場合は長時間)。
  • パーティBは、処理された画像にリンクする201 Createdヘッダーを含むHTTP Content-Location: <url>応答メッセージをパーティAに送信します。
  • パーティーAは、処理された画像への参照を持っているため、その作業は完了したと見なします
  • パーティーAが処理された画像を必要とする将来のある時点で、以前のContent-Locationヘッダーからのリンクを使用して画像を取得

201 Created応答コードは、要求が成功しただけでなく、新しいリソースも作成したことをクライアントに通知します。 201応答では、Content-Locationヘッダーは作成されたリソースへのリンクです。これは、RFC 7231のセクション6.3.2および3.1.4.2で指定されています。

ここで、この相互作用がAMQP上の架空のRPCプロトコルでどのように機能するかを見てみましょう。

  • パーティーAは、AMQPメッセージブローカー(メッセンジャーと呼びます)に、画像と、処理のためにそれをパーティーBにルーティングするように指示するメッセージを送信し、その後、画像のある種のアドレスでパーティーAに応答します。
  • パーティーAは待機し、おそらく他のことをします
  • メッセンジャーがパーティーAの元のメッセージをパーティーBに送信する
  • パーティBがメッセージを処理する
  • パーティーBはメッセンジャーに、処理された画像のアドレスを含むメッセージと、そのメッセージをパーティーAにルーティングするための指示を送信します
  • メッセンジャーがパーティーAに、処理された画像アドレスを含むパーティーBからのメッセージを送信する
  • パーティーAは、処理された画像への参照を持っているため、その作業は完了したと見なします
  • 将来、パーティAが画像を必要とするときに、アドレスを使用して画像を取得します(おそらく他のパーティにメッセージを送信することにより)。

ここで問題がわかりますか?どちらの場合も、パーティAはafterパーティBがイメージを処理するまで)イメージアドレスを取得できません。それでも、パーティーAはすぐに画像を必要とせず、すべての権利により、処理がまだ完了していれば、あまり気になりませんでした。

AMQPケースでは、パーティBからAにB accepted処理するイメージを伝え、Aに処理後のイメージのアドレスwill beを処理させることで、これをかなり簡単に修正できます。完了します。次に、パーティBは、画像処理が終了したことを示すメッセージを将来のいつかAに送信できます。救助へのAMQPメッセージング!

推測するものを除いて:RESTでも同じことができます。 AMQPの例では、「ここで処理された画像です」というメッセージを「画像は処理中です。後で取得できます」というメッセージに変更しました。 RESTful HTTPでこれを行うには、202 AcceptedコードとContent-Locationを再度使用します。

  • パーティAがHTTP POSTメッセージをパーティBにContent-Type: image/jpegで送信します。
  • パーティBは、処理が終了したかどうか、処理が完了したときに画像を利用できる場所を説明する何らかの「非同期操作」コンテンツを含む202 Accepted応答をすぐに返信します。また、Content-Location: <link>ヘッダーも含まれます。これは、202 Accepted応答で、応答の本文が何であっても表されるリソースへのリンクです。この場合、それは非同期操作へのリンクであることを意味します!
  • パーティーAは処理されたイメージへの参照を持っているため、その作業は完了したと見なします
  • パーティーAが処理された画像を必要とする将来のある時点で、最初にContent-Locationヘッダーにリンクされている非同期操作リソースを取得して、処理が終了したかどうかを判断します。その場合、パーティAは非同期操作自体のリンクを使用して、処理された画像を取得します。

ここでの唯一の違いは、AMQPモデルでは、パーティーBがパーティーAに画像処理の完了を通知することです。ただし、RESTモデルでは、パーティーAは、実際に画像が必要になる直前に処理が行われたかどうかを確認します。 これらのアプローチは同等にスケーラブルです。システムが大きくなるにつれて、非同期AMQPと非同期RESTの両方の戦略で送信されるメッセージの数は、同等の漸近的な複雑さで増加します。唯一の違いは、クライアントがサーバーではなく追加のメッセージを送信することです。

しかし、RESTアプローチには、いくつかのトリックがあります。動的な検出とプロトコルのネゴシエーション。同期と非同期の両方のREST相互作用がどのように開始されたかを検討します。パーティーAがまったく同じ要求をパーティーBに送信しましたが、唯一の違いは、パーティーBが応答した特定の種類の成功メッセージです。パーティーAがchoose画像処理が同期か非同期かを希望した場合はどうなりますか?パーティBが非同期処理を実行できるかどうかをパーティAが知らない場合はどうなりますか?

さて、HTTPは実際にこれのために標準化されたプロトコルをすでに持っています!これはHTTPプリファレンスと呼ばれ、特にRFC 7240セクション4.1のrespond-asyncプリファレンスです。パーティAが非同期応答を希望する場合、最初のPOST要求にPrefer: respond-asyncヘッダーが含まれます。パーティBがこのリクエストを受け入れることを決定した場合、202 Acceptedを含むPreference-Applied: respond-async応答を返信します。それ以外の場合、パーティBは通常どおりにPreferヘッダーを無視して201 Createdを送り返します。

これにより、パーティAはサーバーとの間でnegotiateを使用して、たまたま通信している画像処理の実装に動的に適応できます。さらに、明示的なリンクを使用することで、パーティーAはB以外のパーティーについて知る必要がないことを意味します。AMQPメッセージブローカーも、イメージアドレスを実際にイメージデータに変換する方法を知っている不可解なパーティーCも、2番目のB非同期もありません。同期要求と非同期要求の両方を行う必要がある場合は、パーティーなど。必要なもの、必要に応じて必要なものを記述し、ステータスコード、応答コンテンツ、リンクに反応します。 Cache-Controlヘッダーを追加して、データのローカルコピーをいつ保持するかを明示的に指示します。サーバーは、クライアントがローカル(またはオフラインでも)のコピーを保持できるリソースと交渉できるようになりました。これは、RESTで疎結合のフォールトトレラントマイクロサービスを構築する方法です。

9
Jack

純粋にイベント駆動型である必要があるかどうかは、もちろん、特定のシナリオに依存します。あなたが本当にそうである必要があると仮定すると、次の方法で問題を解決できます:

データのローカルの読み取り専用コピーを保存するさまざまなイベントをリッスンし、ペイロードに情報をキャプチャします。これにより、その正確なアプリケーションに適した形式で保存された、そのデータの読み取りが高速になりますが、サービス全体でデータが最終的に一貫することも意味します。

このアプローチでGET /users/1をモデル化するには、UserCreatedおよびUserUpdatedイベントをリッスンし、ユーザーデータの有用なサブセットをサービスに格納します。ユーザー情報を取得する必要がある場合は、ローカルデータストアにクエリを実行するだけです。

少しの間、/users/エンドポイントを公開するサービスがいかなる種類のイベントも公開しないと仮定しましょう。この場合、作成したHTTPリクエストへの応答をキャッシュするだけで同様のことを実現できるため、一定の時間内にユーザーごとに複数のHTTPリクエストを作成する必要がなくなります。

1
Andy Hunt

イベントソースシステムの場合、非同期の側面は通常、状態を表す何か、おそらくデータベース、またはいくつかのデータの集約ビューが変更されたときに機能します。この例を使用すると、GET/api/usersの呼び出しは、システム内のユーザーのリストの最新の表現を持つサービスからの応答を返すだけです。別のシナリオでは、GET/api/usersへのリクエストにより、サービスがユーザーの最後のスナップショット以降のイベントのストリームを使用して別のスナップショットを作成し、単に結果を返す可能性があります。イベント駆動型システムは、必ずしも要求から応答まで完全に非同期であるとは限りませんが、サービスが他のサービスと対話する必要があるレベルにある傾向があります。多くの場合、非同期でGET要求を返すことは意味がないため、応答の計算方法に関係なく、単にサービスの応答を返すことができます。

0
Lloyd Moore