APIゲートウェイパターンに従ってREST APIを介して公開する機能を持つマイクロサービスの束があります。これらのマイクロサービスはSpring Bootアプリケーションであるため、Spring AMQPを使用してRPCスタイルの同期通信を実現していますこれらのマイクロサービスの間で、順調に進んでいます。しかし、イベントドリブンマイクロサービスアーキテクチャについて読み、Spring Cloud Streamなどのプロジェクトを検討するほど、RPCで間違った方法を実行している可能性があると確信するようになります。 、同期アプローチ(特に、クライアントアプリケーションからの1秒あたり数百または数千のリクエストに応答するためにこれをスケーリングする必要があるため)。
イベント駆動型アーキテクチャの背後にあるポイントを理解しています。私がよく理解していないのは、すべての要求に対する応答を期待するモデル(REST)の後ろに座っているときに、このようなパターンを実際に使用する方法です。たとえば、APIゲートウェイをマイクロサービスとして使用し、ユーザーを保存および管理する別のマイクロサービスを使用している場合、GET /users/1
純粋にイベント駆動の方法で?
私の後に繰り返して:
RESTおよび非同期イベントは代替手段ではありません。これらは完全に直交しています。
どちらか、または両方、あるいは両方を持たせることができます。それらは、まったく異なる問題ドメインのためのまったく異なるツールです。実際、汎用の要求と応答の通信は、非同期で、イベント駆動型で、フォールトトレラントであることが絶対に可能です。
簡単な例として、AMQPプロトコルはTCP接続を介してメッセージを送信します。 TCPでは、すべてのパケットが受信者によって確認される必要があります。パケットの送信者がそのパケットのACKを受信しない場合、ACKが送信されるまで、またはアプリケーション層が「放棄」して接続を放棄するまで、そのパケットを再送信し続けます。すべての "パケット送信要求" mustには付随する "パケット確認応答"があり、応答に失敗すると接続全体が失敗するため、これは明らかにフォールトトレラントでない要求応答モデルです。しかし、非同期フォールトトレラントメッセージングのために標準化され広く採用されているプロトコルであるAMQPは、TCPを介して通信されます。何ができますか?
ここで中心となる概念は、スケーラブルな疎結合のフォールトトレラントメッセージングが送信するメッセージではなく、送信方法によって定義されることです。つまり、疎結合はアプリケーション層で定義されますです。
RESTful HTTPと直接通信するか、AMQPメッセージブローカーと間接的に通信する2つのパーティを見てみましょう。パーティーAがパーティーBにJPEG画像をアップロードしたいとします。パーティーBは画像を鮮明化、圧縮、またはその他の方法で強化します。パーティーAは、処理された画像をすぐに必要としませんが、将来の使用と取得のために参照する必要があります。 RESTで使用できる方法の1つは次のとおりです。
POST
要求メッセージをパーティBにContent-Type: image/jpeg
で送信します。201 Created
ヘッダーを含むHTTP Content-Location: <url>
応答メッセージをパーティAに送信します。Content-Location
ヘッダーからのリンクを使用して画像を取得201 Created
応答コードは、要求が成功しただけでなく、新しいリソースも作成したことをクライアントに通知します。 201応答では、Content-Location
ヘッダーは作成されたリソースへのリンクです。これは、RFC 7231のセクション6.3.2および3.1.4.2で指定されています。
ここで、この相互作用がAMQP上の架空のRPCプロトコルでどのように機能するかを見てみましょう。
ここで問題がわかりますか?どちらの場合も、パーティAはafterパーティBがイメージを処理するまで)イメージアドレスを取得できません。それでも、パーティーAはすぐに画像を必要とせず、すべての権利により、処理がまだ完了していれば、あまり気になりませんでした。
AMQPケースでは、パーティBからAにB accepted処理するイメージを伝え、Aに処理後のイメージのアドレスwill beを処理させることで、これをかなり簡単に修正できます。完了します。次に、パーティBは、画像処理が終了したことを示すメッセージを将来のいつかAに送信できます。救助へのAMQPメッセージング!
推測するものを除いて:RESTでも同じことができます。 AMQPの例では、「ここで処理された画像です」というメッセージを「画像は処理中です。後で取得できます」というメッセージに変更しました。 RESTful HTTPでこれを行うには、202 Accepted
コードとContent-Location
を再度使用します。
POST
メッセージをパーティBにContent-Type: image/jpeg
で送信します。202 Accepted
応答をすぐに返信します。また、Content-Location: <link>
ヘッダーも含まれます。これは、202 Accepted
応答で、応答の本文が何であっても表されるリソースへのリンクです。この場合、それは非同期操作へのリンクであることを意味します!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で疎結合のフォールトトレラントマイクロサービスを構築する方法です。
純粋にイベント駆動型である必要があるかどうかは、もちろん、特定のシナリオに依存します。あなたが本当にそうである必要があると仮定すると、次の方法で問題を解決できます:
データのローカルの読み取り専用コピーを保存するさまざまなイベントをリッスンし、ペイロードに情報をキャプチャします。これにより、その正確なアプリケーションに適した形式で保存された、そのデータの読み取りが高速になりますが、サービス全体でデータが最終的に一貫することも意味します。
このアプローチでGET /users/1
をモデル化するには、UserCreated
およびUserUpdated
イベントをリッスンし、ユーザーデータの有用なサブセットをサービスに格納します。ユーザー情報を取得する必要がある場合は、ローカルデータストアにクエリを実行するだけです。
少しの間、/users/
エンドポイントを公開するサービスがいかなる種類のイベントも公開しないと仮定しましょう。この場合、作成したHTTPリクエストへの応答をキャッシュするだけで同様のことを実現できるため、一定の時間内にユーザーごとに複数のHTTPリクエストを作成する必要がなくなります。
イベントソースシステムの場合、非同期の側面は通常、状態を表す何か、おそらくデータベース、またはいくつかのデータの集約ビューが変更されたときに機能します。この例を使用すると、GET/api/usersの呼び出しは、システム内のユーザーのリストの最新の表現を持つサービスからの応答を返すだけです。別のシナリオでは、GET/api/usersへのリクエストにより、サービスがユーザーの最後のスナップショット以降のイベントのストリームを使用して別のスナップショットを作成し、単に結果を返す可能性があります。イベント駆動型システムは、必ずしも要求から応答まで完全に非同期であるとは限りませんが、サービスが他のサービスと対話する必要があるレベルにある傾向があります。多くの場合、非同期でGET要求を返すことは意味がないため、応答の計算方法に関係なく、単にサービスの応答を返すことができます。