RabbitMQ の 5 つの一般的なモデルと SpringAMQP の使用の紹介

MQ の概要

MQ、MessageQueneはメッセージキューであり、プログラム間の异步通信メソッドです。
MQ を使用する理由
MQ はプログラム間の非同期通信に使用されますが、なぜ MQ を使用するのかという問題は、なぜプログラム間の非同期通信を導入する必要があるのか​​ということでもあります。
同期通信とは何ですか?
同期通信を簡単に理解すると、次の行で実行されるコードは、完全执行前の行が完了するまで実行できないということになります。例: 注文 ID に従ってユーザー情報をクエリし、それを返したいとします。このとき、まず注文システムを呼び出してIDを介して注文情報を照会し、注文情報からユーザーIDを取得し、ユーザーシステムを呼び出してユーザーIDを渡してユーザー情報を照会して返す必要があります。
ユーザー情報を照会したい場合は、まず注文情報を確認する必要があります。コードの次の行の実行は、コードの前の行の実行結果に依存します。通常、コード内では同期通信を使用します。これはステップごとに実行されます。上から下にステップし、ジャンプします。async を axios に追加するときと同じです。
同期通信を使用する例は不合理です: 注文と支払いの後、注文システムは支払いが完了するのを待ってから、ストレージ システムを呼び出して在庫の差し引きなどの操作を実行します。ストレージ システムが正常に実行された後、すべての実行が成功すると、注文成功情報がユーザーに返されます。
同期通信: 各システムが連鎖的に実行される 一部が壊れるとすべてが壊れる 追加や削除には修正が必要 非同期
ここに画像の説明を挿入
通信: 次のコード行は前のコード行の実行結果を待つ必要がない、呼び出されている限り、最終的に実行されます。サブスレッドやaxiosなどと同様です。
ここに画像の説明を挿入
この例の同期通信と非同期通信の比較を通じて、同期によって引き起こされる問題を見つけることができますか?

  • 性能和吞吐能力下降: これらすべてのシステムが順番に正常に実行されるまで、ユーザーは応答を受け取りません。ユーザーエクスペリエンスが大幅に低下します。レストランで食べ物を注文するのと同じように、顧客が料理を食べるように指示している限り、ウェイターは注文が完了するまで待つ代わりに、注文が成功したことを顧客に伝えることができます(まだ完了していませんが、成功する可能性があります)。料理が提供されると、ウェイターは注文が成功したことを顧客に伝え、ユーザーは注文に成功します。これにより、ユーザーはカリフラワーを注文するのに時間がかかりすぎると感じてしまいます。前の例に戻ります。ユーザーが支払いを行った後、注文が成功したことを確認し、ユーザーに応答メッセージを返すことがすでに可能です。成功が保証されている限り、残りの操作はバックグラウンドに引き渡すことができ、ユーザーは待つ必要がありません。

  • 资源浪费: ユーザーの支払いが成功した後、ユーザーの結果が期限内に返されないだけでなく、このスレッドは他のシステムの呼び出しにも参加するため、スレッド リソースが無駄になります。

  • 耦合度高: 倉庫システムと物流システムにショート メッセージ システムが追加される場合、注文システムのコードが変更され、コード結果全体にショート メッセージ システムが挿入されます。非同期通信を使用するには、独立した SMS システムを開発し、ミドルウェアに接続し、注文システムからの統合通知をリッスンするだけです。
    呼び出し側はキューに情報を送信することだけを担当し、呼び出される側はメッセージキューからメッセージを取り出して呼び出しを行うだけの役割を果たし、両者はこの仲介者(メッセージキュー)を通じてコードの分離を実現します。
    ここに画像の説明を挿入
    级联失败注:同期通信の場合、この処理中にエラーが発生すると、以下のコードが実行されず、業務全体でエラーが発生します。しかし、非同期通信では、企業はミドルウェアを通じてメッセージを取得し、それらの間にはつながりがありません。一つの業務遂行に失敗しても、他の業務に影響を与えることはありません。

概要:
非同期通信を使用すると、ユーザーの操作に迅速に応答し (実行は完了していませんが、最終的な実行は確実に成功します)、次のユーザーのためにリソースを解放できます。非同期サービスは比較的独立しており、ミドルウェアから一律に呼び出されるため、特定のサービスの追加や削除に非常に便利であると同時に、サービスに障害が発生しても他のサービスに影響を与えることがありません。

非同期通信は非常に優れているのに、なぜ同期通信を使用するのでしょうか? :
前の例でも、後続の呼び出しはすべて支払い結果を待っているため、支払いシステムは注文システム内で同期的に呼び出される必要があります。非同期通信を使用し、最終的にコードの下で結果を待つ場合は、同期通信の呼び出しほど高速ではない可能性があります时效性

非同期通信を実現するにはどうすればよいでしょうか?
サービスの場合は、サブスレッドを開始してそれを呼び出すことができますが、これはユーザーの応答速度を向上させるだけです。サービスがたくさんある場合はどうなるでしょうか? サービスの動的スケーリングについてはどうですか? これにはメッセージキューが必要です。
通常、呼び出しメッセージはキューに送信され、各呼び出し先はキュー内のメッセージをリッスンし、キューにメッセージが存在すると、メッセージ キューからパラメータを取得して、対応するメソッドを実行します。

MQ を使用する利点

解耦: サービス呼び出し間でプラグイン可能な効果が得られ、各サービスは互いに分離されており、相互に影響を与えません。 :
异步後続の処理を待つ必要がないため、システムのスループットが向上し、応答速度が向上し、ブロッキングが減少します
削峰。 : 大規模なトラフィックが発生します。各サービスで最初にメッセージキューによってブロックされる フロントでは、バッファとしてメッセージをキューに保持し、ダムのように適切な速度でキューを呼び出すことができます
。洪水を貯水池に貯めておき、適切な速度で放流する緩衝材の役割を果たします。

MQ を使用するデメリット:

  • システムの複雑さは増大しており、反復消費の問題、順序の問題、メッセージの信頼性の問題、デッドレターの問題などの一連の問題が発生しています。ビジネス プロセスは、同期呼び出しよりも混沌としています。
  • ユーザビリティの問題: MQ を導入する場合、MQ が正常に使用できることを確認する必要があります。非同期呼び出しは、MQ のパフォーマンス、セキュリティ、および可用性に依存します。もう一つのリスク
  • 一貫性の問題

: 各サービスを呼び出すのは MQ ではありませんが、各サービスは MQ メッセージをリッスンし、コールバックを通じて対応するメソッドを呼び出し
ます

  • サービス間の呼び出しはサービス レジストリを経由しないのでしょうか? サービス センター経由でのリボン コールの開始は同期コールです
  • MQ を使用してサービス インスタンスの呼び出しを実現するにはどうすればよいですか? インスタンスに対して複数のサービス呼び出しを行っていますか?

技術的な比較

一般的な MQ はRabbit MQ、、、、ですこれらのテクノロジーではRocket MQActive MQKafkaActive MQ较弱

ラビットMQ アクティブMQ ロケットMQ カフカ
会社/コミュニティ うさぎ アパッチ アリ アパッチ
開発言語 Erlang ジャワ ジャワ スカラ&Java
プロトコルのサポート AMQP、XMPP、SMTP、STOMP OpenWire、STOMP、REST、XMPP、AMQP カスタムプロトコル カスタムプロトコル
可用性 一般的
スタンドアロンのスループット 一般的 違い 高い 非常高
メッセージの遅延 微秒级 ミリ秒 ミリ秒 ミリ秒以内
メッセージの信頼性 一般的 一般的

RabbitMQ と RocketMQ は、 Kafka との消息延迟間のトレードオフであり吞吐量、最大のハイライトは高吞吐通常ビッグデータで使用されることです。

RabbitMQ の使用

ドッカーの起動

コマンドを使用する

docker run \
 -e RABBITMQ_DEFAULT_USER=用户名 \
 -e RABBITMQ_DEFAULT_PASS=密码 \
 --name mq \
 --hostname myhost \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 Rabbit镜像id

RabbitMQ は 2 つのポートを公開しており、15672 は視覚的な管理を提供するブラウザ アクセスに使用され、5672 はさまざまなサービスによる接続に使用されます。
ブラウザから15672にアクセスし、設定したユーザー名とパスワードを入力して管理ページに移動します
ここに画像の説明を挿入

管理パネルには、表示だけでなく、キューへのメッセージの追加、キュー メッセージの表示、キューの追加、キューの管理、スイッチの追加、スイッチの管理など、多くの機能があります。

MQ の役割の紹介

ここに画像の説明を挿入
MQ の最も基本的なものは、図の 4 つの役割です
publisher。メッセージ パブリッシャーは各サービスの呼び出し元です。つまり、メッセージをメッセージ キューに送信し、他のサービス (次のコンシューマー) はコールバックをリッスンします。スイッチ
exchangeメッセージを個々のキューにブロードキャストする責任があります。凹レンズが光を発散させて、メッセージ発行者のメッセージをサブスクライブされたキューにブロードキャストするように感じられます。同じメッセージが指定されたキューに送信されることに注意してください。パブリッシャが 1 つのキューにのみメッセージを送信する場合、交換は必要ありません。交換がある場合、キューとのパブリッシュおよびサブスクライブ関係が存在します。スイッチはデータをキャッシュできず、キューへの送信プロセスが失敗し、メッセージは直接失われます。
quene: メッセージキュー。コンシューマにメッセージをプッシュし、メッセージを一時的に保存する機能があります。キュー内のメッセージは 1 つのコンシューマによってのみ消費できます。
consumer: メッセージ コンシューマ。コンシューマはキューからメッセージを取得し、内部でキュー内のメッセージを監視し、監視対象のキューにメッセージが存在するとすぐにコールバックして、メソッドを呼び出す目的を達成する必要があります。サービス。

サービスはキューを使用し、サービス内の各インスタンスはキューの下でコンシューマとして機能します。

5 つの一般的なメッセージ モデル

複数のプロデューサが存在することもできますが、モデルでは、同じメッセージ形式でキューに送信される限り、1 つと複数は同じです。わかりやすくするために、1 つのインスタンスを使用してスイッチなしを表します

コンシューマー はい一个服务下有多或一个实例、各インスタンスは同じメッセージを消費するために競合します。

  • 1、基本消息队列: 1 つのキューと 1 つのコンシューマー。これは、1 つのサービスの下で 1 つのインスタンスのみに相当します。
    ここに画像の説明を挿入
  • 2、工作消息队列: キューは複数のコンシューマに対応します。これは、1 つのサービスの下に複数のインスタンスがあることと同じです。
    ここに画像の説明を挿入

スイッチがあり
、スイッチは各キューにメッセージを送信して、各サービスに対応するインスタンスに通話情報を送信できますが、
スイッチのメッセージを取得できるキューはどうなるのでしょうか? 共通するのは3つ

  • 广播 Fanout: ホスト下のすべてのキューがこのスイッチの情報を取得する限り、このホスト下のすべてのサービスでインスタンスを呼び出すことと同等です。
    ここに画像の説明を挿入

  • 路由 Direct:完美対応するキューのみがメッセージを受信できます
    ここに画像の説明を挿入

  • 主题 Topic: 一致するキューのみがメッセージを受信できます。前のものとは異なり、一致には 1 対 1 の対応は必要ありません。範囲またはレベルを指定できます.

ここに画像の説明を挿入

考察: RabbitMQ サーバー内にサービス発行者と利用者のグループが複数ある場合はどうすればよいでしょうか? 言い換えれば、複数の同一かつ異なるビジネス キュー モデルをどのように分離するかということです。
1 つは RabbitMQ に導入されておりVirtualHost、これによって効果が実現されます多租户。つまり、RabbitMQ は論理的に複数の小さな RabbitMQ に分割され、VirtualHostそれらは完全に分離されます。
したがって、接続するときは、RabbitMQ の IP とポートを指定するだけでなく、どれが特定のものであるかも指定する必要があります。VirtualHost
ここに画像の説明を挿入

Spring AMQP テンプレートを使用する

AMQP とは: AMQP は、ユニファイド メッセージング サービスを提供するアプリケーション層標準の高度なメッセージ キュー プロトコルであり、メッセージ指向ミドルウェア用に設計されたアプリケーション層プロトコルのオープン標準です。このプロトコルに基づくクライアントおよびメッセージミドルウェアはメッセージを送信でき、異なるクライアント/ミドルウェア製品、異なる開発言語、その他の条件によって制限されません。

SpringAMQP はコード内での RabbitMQ の使用を簡素化します

  1. プロデューサーとサーバーの間に依存関係を導入するには
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
  1. VirtualHostRabbitMQ サービスの IP とポート、および対応する(デフォルト/)を構成ファイルで指定します。
spring:
  rabbitmq:
    host: 192.168.83.100 
    port: 5672
    virtual-host: /
    username: yan
    password: 1234

シミュレーションデモ処理では、コンシューマが監視対象のメッセージを直接出力しますが、実際のアプリケーションでは、メッセージをパラメータとしてサービスメソッドを呼び出すコールバックを利用して実現します异步调用

単純なキューモデル

単純なメッセージ モデルでは、メッセージの送信には を知る必要があり队列名字、コンシューマは次に従って队列名字キューをリッスンするかどうかを決定します。

  • プロデューサー

  • 通常、プロデューサーはサービス ビジネスのキューにメッセージを送信し、コントローラーはサービスを呼び出します。ここでは、簡単にするためにコントローラーにビジネスを直接記述します。
    ここに画像の説明を挿入

  • 消費者
    ここに画像の説明を挿入

このメソッドではキューの宣言が必要であることに注意してください。
ここに画像の説明を挿入

ワークキューモデル

つまり、1 つのキュー、複数のコンシューマ

ここに画像の説明を挿入
キューには複数のコンシューマーが存在しますが、メッセージはどのように分散されるのでしょうか? 1 つのコンシューマがメッセージを消費した後、もう 1 つのコンシューマがそれを再度消費しますか?
例: キューに 10 個のメッセージがある場合、コンシューマー A と B の両方がキューをリッスンします。コンシューマは、平均して最初にメッセージを取り出してから消費しますメッセージがフェッチされると、他のコンシューマはそのメッセージを消費できなくなります。
消費する前に平均的に取り出すには問題があります。パフォーマンスの悪いメッセージとパフォーマンスの良いメッセージが多数取り出されますが、同じ量のメッセージの消費時間が異なり、パフォーマンスの良いアイドル状態のコンシューマとパフォーマンスの低いアイドル状態のコンシューマが存在します。 . 消費者はそれを扱うことができません。全体的なサービスの処理効率が低下するため、処理能力に応じてメッセージをフェッチできるようにする必要があります。
処理能力に応じて取り出したらどうなるでしょうか?
ビュッフェを食べるようなもので、食欲の少ない人も食欲の多い人も同じ量を摂取するのですが、食欲に合わせて摂取させるためには、事前に食べさせてから摂取する必要があります。消費キューについても同様で、各コンシューマは毎回 1 つのメッセージのみを取得できます (数は設定可能)。処理が完了すると、次のメッセージを取得しに行きます。より合理的であり、全体の処理効率が向上します。
設定を変更します:
ここに画像の説明を挿入

ブロードキャストモデルのファンアウト

スイッチは、メッセージを各キューに分散するために導入されました。
ここに画像の説明を挿入
コンシューマは引き続きキュー名を指定してキューからメッセージを取得しますが、キューはプロデューサーに直接接続されるのではなく、スイッチに接続されますパブリッシャはエクスチェンジに情報を送信し、エクスチェンジはバインドされたキューに情報をブロードキャストします。
したがって、プロデューサはエクスチェンジを宣言し、キューを宣言し、エクスチェンジとキューをバインドして、最後にメッセージをエクスチェンジに送信する必要があります。

ここで、fan1という名前の交換は、という名前の 2 つのキューと交換をバインドするために使用され、各キューにはコンシューマ がありますq1q2メッセージを交換局に送信すると、両方のキューにあるコンシューマーがメッセージを受信します。

方法 1: @Bean メソッドを使用してキューをスイッチにバインドする


消費者側
ここに画像の説明を挿入


ここに画像の説明を挿入
引き続きプロデューサー側のキューからメッセージを取得します。
プロデューサーは引き続きメッセージをキューに送信します。
ここに画像の説明を挿入

プロセスの概要:
ここに画像の説明を挿入
方法 2 : アノテーションの使用
プロデューサは引き続きメッセージをスイッチに送信しますが、キューを管理してスイッチをキューにバインドする必要はありません。コンシューマはキューをリッスンするときに、
そのスイッチを指定して関係をバインドします。キューとスイッチの間

ここに画像の説明を挿入

スイッチの概要:

  1. スイッチを使用して、プロデューサーはメッセージをスイッチに送信したいと考えています。
  2. スイッチはキューにバインドされている必要があります

:
3. スイッチはデータをキャッシュできません。スイッチがメッセージをキューにブロードキャストできない場合、メッセージは失われます。

ルーティングモードダイレクト

ブロードキャスト モードとは異なり、スイッチにバインドされているすべてのキューが、スイッチによってプッシュされたメッセージ、およびこれに基づいてパスワード ( ) とrouting Keyスイッチ当前消息のパスワード ( routing Key)を取得できる必要はありません完全匹配このモードでは、スイッチがバインドされたキュー内の特定のキューにメッセージをブロードキャストできるようになります。
例: 今回は注文システムでメッセージを送信するときに倉庫システムのみを呼び出したいのですが、スイッチにバインドされている物流システムもあります。これをメッセージで指定する必要があり、一致するキューのみを指定する必要があります。取得できるためroutingKey、呼び出しがより柔軟になります。

使用方法 Faout モードでは、メッセージ送信時にプロデューサー側が指定しますroutingKey
ここに画像の説明を挿入

コンシューマは、キューとスイッチをバインドするときに routingKey を指定します。
ここに画像の説明を挿入

DIRCT モードでは、キューはスイッチ内のデータが満たす条件を取得しようとします。

  1. スイッチにバインドする
  2. キューにはroutingKey交換にバインドされたメッセージが含まれていますroutingKey

routingKey が同じでも、listen キューにバインドされているスイッチが異なるため、データを取得できません
ダイレクトモード 各キューの routingKey にこのメッセージの routingKey が含まれる場合、Faout モードになります。

テーマモード トピック

ダイレクトモードと比較して、このモードでは routingKey が使用できます通配符それぞれは.レベル全体のレベルを表し、任意のレベルを表します*: = = but ! =は 1 つのレベル (1 つの単語) しか表現できないため、ここでは少なく、1 つのみであることに注意してください。#
a.b.ca.*.ca.#a.b.ca.**
*

この種のワイルドカードは、プロデューサのメッセージだけでなく、コンシューマ バインディング キューでも指定できます。@Bean の方法で宣言してバインドすること
できます。ここでは 1 つだけ示します。
ここに画像の説明を挿入

ここに画像の説明を挿入

ここに画像の説明を挿入

: キューに送信できるのは文字列型のメッセージだけではなく、すべての種類のメッセージがサポートされています。オブジェクトをメッセージとして送信する場合、オブジェクトの送信のためにシリアル化する必要があります。

では、オブジェクト データをメッセージ キューに渡すにはどうすればよいでしょうか?
送信者はオブジェクトを JSON 文字列形式に変換してオブジェクトをシリアル化し、コンシューマーは JSON 文字列をオブジェクトに逆シリアル化できます。

springAMQP はスイッチとキューを自動的に作成できますが、プロデューサーはキューとスイッチに送信する前にそれらが存在することを確認する必要があります。ステートメントは消費者側でも生産者側でも構いませんが、ビジネスの増減が発生したときに生産者が気づかないように、消費者側で行う必要があります。キュー、スイッチ、およびそれらのバインディング関係の宣言は、注解この方法で宣言することも、コンテナーで注入bean宣言することもできます。

RabbitMQ では、スイッチを作成してタイプを指定すると、仮想マシンを削除しない限りスイッチを変更することはできません。例:
a1 という名前のスイッチを作成し、faout タイプとして指定しましたが、a1 を変更したいと考えています。この時点では失敗しており、a1 スイッチはまだ faout タイプであるため、このようなエラーが報告されます。解決策は、inequivalent arg 'type' for exchange 'e1' in vhost '/': received 'direct' but current is 'fanout'
スイッチの名前を変更するか、削除して新しいタイプを作成することです。

おすすめ

転載: blog.csdn.net/m0_52889702/article/details/128443290