目次
6.3. パブリッシュ/サブスクライブ (パブリッシュおよびサブスクライブ モード)
1.MQメッセージミドルウェア
MQ の正式名は Message Queue (メッセージ キュー) で、メッセージの送信プロセス中にメッセージを保存するコンテナーです。アプリケーション間の通信方法です。
1.2. MQ を使用する理由
このプロジェクトでは、即時復帰を必要としない時間のかかる一部の操作を抽出して非同期処理することで、サーバーのリクエスト応答時間を大幅に短縮し、システムのスループットを向上させます。
2. MQ のメリットとデメリット
2.1. 利点
2.1.1. アプリケーションのデカップリング
電子商取引アプリケーションを例にとると、アプリケーションには注文システム、在庫システム、物流システム、支払いシステムなどが含まれます。ユーザーが注文を作成した後、在庫システム、物流システム、決済システムが連携して呼び出された場合、いずれかのサブシステムに障害が発生すると、注文動作が異常になります。メッセージ キュー ベースのアプローチに変換すると、システム間呼び出しの問題が大幅に軽減され、たとえば、物流システムの障害による修復には数分かかります。この数分間の間に、物流システムによって処理されるコンテンツはメッセージ キューにキャッシュされ、ユーザーの注文操作は正常に完了できます。物流システムが復旧すると、注文情報の処理が継続できるため、中間ユーザーは物流システムの障害を感じることがなくなり、システムの可用性が向上します。
2.1.2. 非同期の高速化
上記の注文を完了するまでにかかる時間: 20 + 300 + 300 + 300 = 920 ミリ秒。ユーザーが注文ボタンをクリックした後、注文の応答を得るまでに 920 ミリ秒待つ必要がありますが、これでは遅すぎます。
MQ を使用すると上記の問題を解決できます
ユーザーが注文ボタンをクリックした後、注文応答を受け取るまでに必要な時間は 25 ミリ秒だけです (20 + 5 = 25 ミリ秒)。ユーザー エクスペリエンスとシステム スループット (単位時間あたりに処理されるリクエストの数) が向上します。
2.1.3. ピークの削りと谷の埋め込み
例えば、注文システムが最大1,000件の注文を処理できる場合、通常期の注文には十分な処理能力があり、通常期であれば注文後1秒で結果を返すことができます。ただし、繁忙期に 2,000 件の注文が発生すると、OS では対応できず、1,000 件を超える注文数を制限し、ユーザーが注文できないようにすることしかできません。メッセージキューをバッファとして使用することで、この制限を解除し、1 秒以内に行われた注文を処理時間内に分散させることができます。このとき、一部のユーザーは注文後 10 秒以上経過しても正常な注文操作を受信できない場合があります。でも、注文できないよりはいいし、一度の体験の方が良いです。簡単に言うと、アクセス数が急激に増えてもアプリを停止できない場合、例えば「ダブルイレブン」で注文する人が多いのにタオバオアプリを起動する必要があるため、メッセージを利用することができます。フォームは突然の訪問によるストレスを軽減します。
MQ を使用すると、メッセージの消費速度が 1000 に制限されます。これにより、ピーク期間中に生成されたデータが MQ にバックログされ、ピーク期間が「カット」されます。ただし、メッセージのバックログにより、ピーク期間の後の期間では、メッセージのバックログが消費されるまで、メッセージの消費速度は 1,000 のままであり、これを「谷を埋める」と呼ばれます。
MQ を使用すると、システムの安定性が向上します。
2.2. 欠点
2.2.1. システム可用性の低下
システムに導入される外部依存関係が増えるほど、システムの安定性は悪化します。MQ がダウンすると、ビジネスに影響が及びます。
MQ の高可用性を確保するにはどうすればよいですか?
2.2.2. システムの複雑さの増加
MQ の追加によりシステムの複雑さが大幅に増加し、以前はシステム間で同期リモート呼び出しが行われていましたが、現在は MQ を介して非同期呼び出しが行われるようになりました。
メッセージが繰り返し消費されないようにするにはどうすればよいでしょうか? メッセージ損失にどう対処するか? それでは、メッセージ配信の順序を確認してください。
2.2.3. 一貫性の問題
システム A が業務の処理を完了すると、MQ を介してシステム B、システム C、およびシステム D にメッセージ データを送信します。システム B とシステム C が正常に処理すると、システム D は処理に失敗します。
メッセージ データ処理の一貫性を確保するにはどうすればよいですか?
3. 共通の MQ コンポーネント
現在業界には、RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq などの多くの MQ 製品が存在しており、Redis をメッセージ キューとして直接使用するケースもあります。これらのメッセージ キュー製品にはそれぞれ独自の焦点があります。実際のモデルを選択する際には、自社のニーズと MQ 製品の機能を組み合わせる必要があります。
4. RabbitMQ とは何ですか?
2007 年にリリースされた、AMQP (Advanced Message Queuing Protocol) ベースの再利用可能なエンタープライズ メッセージング システムで、現在最も主流のメッセージ ミドルウェアの 1 つです。
RabbitMQ は、erlang によって開発された AMQP (Advanced Message Queue Advanced Message Queuing Protocol) のオープン ソース実装です。erlang 言語の高い同時実行特性により、優れたパフォーマンスを備えています。本質的にはキュー、FIFO 先入れ先出し方式です。出力され、そこに保存されているコンテンツはメッセージです。
RabbitMQ はメッセージング ミドルウェアであり、メッセージを受け入れて転送します。これは宅配便サイトと考えることができます。荷物を送りたいときは、荷物を宅配便ステーションに置くと、宅配便業者が最終的に荷物を受取人に届けます。このロジックによれば、RabbitMQ は宅配業者です。ステーション、宅配業者が速達の配達をお手伝いします。RabbitMQ と速達ステーションの主な違いは、RabbitMQ は速達メールを処理せず、メッセージ データを受信、保存、転送することです。
5. RabbitMQ の原理
5.1. 用語集
ブローカー: メッセージを受信および配布するアプリケーション。RabbitMQ サーバーはメッセージ ブローカーです。
接続: パブリッシャー/コンシューマーとブローカー間の TCP 接続
チャネル: RabbitMQ にアクセスするたびに Connection を確立すると、TCP Connection 確立のオーバーヘッドが大きくなり、メッセージ量が多い場合には効率が悪くなります。チャネルは、接続内で確立される論理接続です。アプリケーションがマルチスレッドをサポートしている場合、通常、各スレッドは通信用に個別のチャネルを作成します。AMQP メソッドには、クライアントとメッセージ ブローカーがチャネルを識別するのに役立つチャネル ID が含まれているため、チャネルは完全に孤立した。チャネルは軽量の接続として、TCP 接続を確立する際のオペレーティング システムのオーバーヘッドを大幅に削減します。
Exchange : メッセージはブローカーの最初のストップに到達し、分散ルールに従って、クエリ テーブルのルーティング キーと一致し、メッセージをキューに分散します。一般的に使用されるタイプは、ダイレクト (ポイントツーポイント)、トピック (パブリッシュ/サブスクライブ)、およびファンアウト (マルチキャスト) です。
Queue : メッセージは最終的にここに送信され、コンシューマーが受け取るのを待ちます。
バインディング: Exchange とキューの間の仮想接続。バインディングにはルーティング キーを含めることができます。バインディング情報は交換でクエリ テーブルに保存され、メッセージ配信の基礎として使用されます。
仮想ホスト: マルチテナンシーとセキュリティ上の理由から設計されており、AMQP の基本コンポーネントは、ネットワーク内の名前空間の概念と同様の仮想グループに分割されます。複数の異なるユーザーが同じ RabbitMQ サーバーが提供するサービスを利用する場合、複数の vhost を分割し、各ユーザーが自分の vhost に Exchange/Queue などを作成することができます。
6. RabbitMQ 動作モード
RabbitMQ は、シンプル モード、ワーク キュー、パブリッシュ/サブスクライブ パブリッシュおよびサブスクリプション モード、ルーティング ルーティング モード、トピック トピック モード、RPC リモート呼び出しモード (リモート呼び出し、完全な MQ ではありません。まだ紹介しません) の 6 つの動作モードを提供します。公式 Web サイトの対応モードの紹介: RabbitMQ チュートリアル — RabbitMQ
6.1. シンプル(シンプルモード)
上記のモデルには次の概念があります。
P: プロデューサー。メッセージを送信したいプログラムです。
C: コンシューマ: メッセージの受信者は常にメッセージが到着するのを待ちます。
queue: メッセージキュー、図の赤い部分。メールボックスと同様に、メッセージをキャッシュできます。プロデューサーはメッセージをメールボックスに配信し、コンシューマーはそこからメッセージを取得します。
頼る:
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.16.0</version>
</dependency>
プロデューサー:
package simple;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @program: qy-rabbitmq
* @description: 简单生产者
* @author:
* @create: 2023-07-12 16:41
**/
public class SimpleProduct {
public static void main(String[] args) throws Exception{
//创建连接工厂并设置连接信息
ConnectionFactory factory = new ConnectionFactory();
//设置rabbitmq服务的地址,默认localhost
factory.setHost("192.168.232.166");
//设置rabbitmq的端口号 AMQP端口
factory.setPort(5672);
//设置账号 默认guest
factory.setUsername("lx");
//设置密码 默认guest
factory.setPassword("lx");
//设置虚拟机 默认 /
factory.setVirtualHost("/lx");
//获取连接对象
Connection connection = factory.newConnection();
//获取channel对象
Channel channel = connection.createChannel();
//创建队列
/*
*String queue, 队列的名称. 如果该名称不存在 则创建 如果存在则不创建
* boolean durable, 该对象是否持久化 当rabbitmq重启后 队列就会消失
* boolean exclusive, 该队列是否被一个消费者独占
* boolean autoDelete,当没有消费者时,该队列是否被自动删除
* Map<String, Object> arguments: 额外参数的设置
* */
//存在该队列则不会创建
channel.queueDeclare("hello_queue",true,false,false,null);
//发送消息
/* String exchange, 交换机的名称 简单模式没有交换机使用""表示采用默认交换机
String routingKey, 路由标识 如果是简单模式起名为队列的名称
BasicProperties props, 消息的属性设置。 设置为null
byte[] body: 消息的内容*/
String msg = "hello rabbitmq------------";
channel.basicPublish("","hello_queue",null,msg.getBytes());
//关闭资源
channel.close();
connection.close();
}
}
消費者:
package simple;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @program: qy-rabbitmq
* @description: 简单消费者
* @author:
* @create: 2023-07-12 18:12
**/
public class SimpleConsumer {
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/lx");
factory.setUsername("lx");
factory.setPassword("lx");
factory.setHost("192.168.232.166");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("hello_queue",true,false,false,null);
//接收队列中的消息
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
*
* @param consumerTag: 消费者的标签
* @param envelope : 设置 拿到你的交换机 路由key等信息
* @param properties: 消息的属性对象
* @param body: 消息的内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收的内容:"+new String(body));
System.out.println("消费者的标志:"+consumerTag);
System.out.println("交换机名称:"+envelope.getExchange());
System.out.println("路由key标志:"+envelope.getRoutingKey());
System.out.println("消息属性:"+properties);
}
};
/**
* String queue, 队列名
* boolean autoAck,是否自动确认。 当rabbitmq把消息发送给消费后,消费端自动确认消息。
* Consumer callback:回调。 当rabbitmq队列中存在消息 则触发该回调
*/
channel.basicConsume("hello_queue",true,consumer);
//不能关闭connection和channel
}
}
6.2. WorkQueues (作業モード)
ワークキュー: エントリープログラムのシンプルモードと比較して、1 つ以上のコンシューマがあり、複数のコンシューマが同じキュー内のメッセージを共同で消費します。適用シナリオ: タスクが重すぎる場合、またはタスクの数が多い場合、ワークキューを使用することでタスクの処理速度を向上させることができます。
プロデューサー:
package work;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @program: qy-rabbitmq
* @description: 工作模式生产者
* @author:
* @create: 2023-07-12 17:11
**/
public class WorkProduct {
public static void main(String[] args) throws Exception{
//创建连接工厂并设置连接信息
ConnectionFactory factory = new ConnectionFactory();
//设置rabbitmq服务的地址,默认localhost
factory.setHost("192.168.232.166");
//设置rabbitmq的端口号 AMQP端口
factory.setPort(5672);
//设置账号 默认guest
factory.setUsername("lx");
//设置密码 默认guest
factory.setPassword("lx");
//设置虚拟机 默认 /
factory.setVirtualHost("/lx");
//获取连接对象
Connection connection = factory.newConnection();
//获取channel对象
Channel channel = connection.createChannel();
//创建队列
/*
*String queue, 队列的名称. 如果该名称不存在 则创建 如果存在则不创建
* boolean durable, 该对象是否持久化 当rabbitmq重启后 队列就会消失
* boolean exclusive, 该队列是否被一个消费者独占
* boolean autoDelete,当没有消费者时,该队列是否被自动删除
* Map<String, Object> arguments: 额外参数的设置
* */
//存在该队列则不会创建
channel.queueDeclare("work_queue",true,false,false,null);
//发送消息
/* String exchange, 交换机的名称 简单模式没有交换机使用""表示采用默认交换机
String routingKey, 路由标识 如果是简单模式起名为队列的名称
BasicProperties props, 消息的属性设置。 设置为null
byte[] body: 消息的内容*/
for (int i = 0; i < 20; i++) {
String msg = "hello work rabbitmq------------";
channel.basicPublish("","work_queue",null,msg.getBytes());
}
//关闭资源
channel.close();
connection.close();
}
}
消費者 × 2 (1 つは下に書かれています):
package work;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @program: qy-rabbitmq
* @description: 工作模式消费者
* @author:
* @create: 2023-07-12 18:15
**/
public class WorkConsumer {
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/lx");
factory.setUsername("lx");
factory.setPassword("lx");
factory.setHost("192.168.232.166");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//接收队列中的消息
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
*
* @param consumerTag: 消费者的标签
* @param envelope : 设置 拿到你的交换机 路由key等信息
* @param properties: 消息的属性对象
* @param body: 消息的内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收的内容:"+new String(body));
}
};
/**
* String queue, 队列名
* boolean autoAck,是否自动确认。 当rabbitmq把消息发送给消费后,消费端自动确认消息。
* Consumer callback:回调。 当rabbitmq队列中存在消息 则触发该回调
*/
channel.basicConsume("work_queue",true,consumer);
//不能关闭connection和channel
}
}
概要: キュー内に複数のコンシューマがある場合、同じメッセージに対するコンシューマ間の関係は競合関係になります。
ワークキュー タスクが重すぎる場合やタスクの数が多い場合、ワークキューを使用するとタスクの処理速度が向上します。たとえば、複数の SMS サービスが展開されている場合、正常に送信する必要があるのは 1 つのノードだけです。
6.3. パブリッシュ/サブスクライブ (パブリッシュおよびサブスクライブ モード)
サブスクリプション モデルには追加の Exchange ロールがあり、プロセスが若干変更されます。
P: プロデューサー、つまり、メッセージを送信したいプログラムですが、メッセージをキューに送信せずに X (スイッチ) に送信します。
C: メッセージの受信者であるコンシューマーは、常にメッセージが到着するのを待ちます。
キュー: メッセージキュー、メッセージの受信とメッセージのキャッシュ
交換:スイッチ(X)。一方では、プロデューサーから送信されたメッセージが受信されます。一方、特別なキューに送信する、すべてのキューに送信する、またはメッセージを破棄するなど、メッセージの処理方法を知っています。これを行う方法は、Exchange の種類によって異なります。Exchange には一般的に 3 つのタイプがあります。
ファンアウト: ブロードキャスト、スイッチにバインドされたすべてのキューにメッセージを配信
直接: 指定されたルーティング キーに一致するキューにメッセージが配信されます。
トピック: ワイルドカード、ルーティング パターン (ルーティング パターン) に一致するキューにメッセージを渡す
Exchange (スイッチ) はメッセージの転送のみを担当し、メッセージを保存する機能がないため、Exchange にバインドされたキューがない場合、またはルーティング ルールに準拠するキューがない場合、メッセージは失われます。
プロデューサー:
package publish;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @program: qy-rabbitmq
* @description: 发布订阅模式生产者
* @author:
* @create: 2023-07-12 17:11
**/
public class PublishProduct {
public static void main(String[] args) throws Exception{
//创建连接工厂并设置连接信息
ConnectionFactory factory = new ConnectionFactory();
//设置rabbitmq服务的地址,默认localhost
factory.setHost("192.168.232.166");
//设置rabbitmq的端口号 AMQP端口
factory.setPort(5672);
//设置账号 默认guest
factory.setUsername("lx");
//设置密码 默认guest
factory.setPassword("lx");
//设置虚拟机 默认 /
factory.setVirtualHost("/lx");
//获取连接对象
Connection connection = factory.newConnection();
//获取channel对象
Channel channel = connection.createChannel();
//创建交换机
/**
* String exchange, 交换机的名称 如果不存在则创建 存在则不创建
* BuiltinExchangeType type, 交换机的类型
* boolean durable: 是否持久化。
*/
channel.exchangeDeclare("publish_exchange", BuiltinExchangeType.FANOUT,true);
//创建队列
//存在该队列则不会创建
channel.queueDeclare("publish_queue01",true,false,false,null);
channel.queueDeclare("publish_queue02",true,false,false,null);
//队列和交换机绑定
/**
* String queue,
* String exchange,
* String routingKey: 发布订阅模式 没有routingkey 则写为”“
*/
channel.queueBind("publish_queue01","publish_exchange","");
channel.queueBind("publish_queue02","publish_exchange","");
//发送消息
String msg = "发布订阅模式广播";
channel.basicPublish("publish_exchange","",null,msg.getBytes());
//关闭资源
channel.close();
connection.close();
}
}
消費者 × 2 (1 つは下に書かれています):
package publish;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @program: qy-rabbitmq
* @description: 发布订阅模式消费者
* @author:
* @create: 2023-07-12 18:15
**/
public class PublishConsumer01 {
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/lx");
factory.setUsername("lx");
factory.setPassword("lx");
factory.setHost("192.168.232.166");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//接收队列中的消息
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
*
* @param consumerTag: 消费者的标签
* @param envelope : 设置 拿到你的交换机 路由key等信息
* @param properties: 消息的属性对象
* @param body: 消息的内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收的内容:"+new String(body));
}
};
/**
* String queue, 队列名
* boolean autoAck,是否自动确认。 当rabbitmq把消息发送给消费后,消费端自动确认消息。
* Consumer callback:回调。 当rabbitmq队列中存在消息 则触发该回调
*/
channel.basicConsume("publish_queue01",true,consumer);
//不能关闭connection和channel
}
}
スイッチはキューにバインドする必要があり、バインド後は複数のコンシューマーがメッセージを受信できるようになります。
パブリッシュ&サブスクライブ モードとワーク キュー モードの違いは次のとおりです。
ワーク キュー モードではスイッチを定義する必要はありませんが、パブリッシュ/サブスクライブ モードではスイッチを定義する必要があります。
パブリッシュ/サブスクライブ モデルのプロデューサーはスイッチにメッセージを送信し、ワーク キュー モデルのプロデューサーはキューにメッセージを送信します (最下層はデフォルトのスイッチを使用します)。
パブリッシュ/サブスクライブ モードでは、キューとスイッチ間のバインドを設定する必要があります。ワーク キュー モードを設定する必要はありません。実際、ワーク キュー モードはキューをデフォルトのスイッチにバインドします。
6.4. ルーティング(ルーティングモード)
キューとスイッチ間のバインディングは任意に行うことはできませんが、RoutingKey を指定する必要があります。
Exchange にメッセージを送信する場合、メッセージの送信者はメッセージの RoutingKey も指定する必要があります。
Exchange は、バインドされた各キューにメッセージを配信するのではなく、メッセージのルーティング キーに基づいて判断するようになり、キューのルーティング キーがメッセージのルーティング キーと完全に一致する場合にのみ、メッセージが受信されます。
P: プロデューサー、Exchange にメッセージを送信します。メッセージを送信するときに、ルーティング キーが指定されます。
X: Exchange (スイッチ)、プロデューサーからメッセージを受信し、ルーティング キーと正確に一致するキューにメッセージを配信します。
C1: コンシューマ。そのキューには、ルーティング キーがエラーである必要があるメッセージが指定されています。
C2: コンシューマ。情報、エラー、警告のルーティング キーを必要とするメッセージをキューで指定します。
プロデューサー:
package routing;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @program: qy-rabbitmq
* @description: 路由模式生产者
* @author:
* @create: 2023-07-12 17:11
**/
public class RouterProduct {
public static void main(String[] args) throws Exception{
//创建连接工厂并设置连接信息
ConnectionFactory factory = new ConnectionFactory();
//设置rabbitmq服务的地址,默认localhost
factory.setHost("192.168.232.166");
//设置rabbitmq的端口号 AMQP端口
factory.setPort(5672);
//设置账号 默认guest
factory.setUsername("lx");
//设置密码 默认guest
factory.setPassword("lx");
//设置虚拟机 默认 /
factory.setVirtualHost("/lx");
//获取连接对象
Connection connection = factory.newConnection();
//获取channel对象
Channel channel = connection.createChannel();
channel.exchangeDeclare("router_exchange", BuiltinExchangeType.DIRECT,true);
//创建队列
//存在该队列则不会创建
channel.queueDeclare("router_queue01",true,false,false,null);
channel.queueDeclare("router_queue02",true,false,false,null);
//队列和交换机绑定
channel.queueBind("router_queue01","publish_exchange","");
channel.queueBind("router_queue02","publish_exchange","");
channel.queueBind("router_queue01","router_exchange","error");
channel.queueBind("router_queue02","router_exchange","error");
//发送消息
String msg = "路由模式";
channel.basicPublish("router_exchange","error",null,msg.getBytes());
//关闭资源
channel.close();
connection.close();
}
}
消費者 × 2 (1 つは下に書かれています):
package routing;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @program: qy-rabbitmq
* @description: 路由模式消费者
* @author:
* @create: 2023-07-12 18:15
**/
public class RouterConsumer01 {
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/lx");
factory.setUsername("lx");
factory.setPassword("lx");
factory.setHost("192.168.232.166");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//接收队列中的消息
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
*
* @param consumerTag: 消费者的标签
* @param envelope : 设置 拿到你的交换机 路由key等信息
* @param properties: 消息的属性对象
* @param body: 消息的内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收的内容:"+new String(body));
}
};
/**
* String queue, 队列名
* boolean autoAck,是否自动确认。 当rabbitmq把消息发送给消费后,消费端自动确认消息。
* Consumer callback:回调。 当rabbitmq队列中存在消息 则触发该回调
*/
channel.basicConsume("router_queue01",true,consumer);
}
}
まとめ:
ルーティング モードでは、スイッチにバインドするときにキューでルーティング キーを指定する必要があり、メッセージはルーティング キーに一致するキューに転送されます。
6.5. トピック(テーマモード)
Direct と比較して、Topic タイプは、RoutingKey に基づいてメッセージを異なるキューにルーティングできます。トピック タイプ Exchange では、ルーティング キーをバインドするときにキューでワイルドカードを使用できるというだけです。
ルーティングキーは通常、「.」で区切られた 1 つ以上の単語で構成されます (例: item.insert)。
ワイルドカード ルール: # は 1 つ以上の単語に一致し、* は 1 つの単語に一致します。たとえば、item.# は item.insert.abc または item.insert に一致し、item.* は item.insert にのみ一致します。
マッチングの仕組みは以下の通りです。
プロデューサー:
package topic;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* @program: qy-rabbitmq
* @description: 主题模式生产者
* @author:
* @create: 2023-07-12 17:11
**/
public class TopicProduct {
public static void main(String[] args) throws Exception{
//创建连接工厂并设置连接信息
ConnectionFactory factory = new ConnectionFactory();
//设置rabbitmq服务的地址,默认localhost
factory.setHost("192.168.232.166");
//设置rabbitmq的端口号 AMQP端口
factory.setPort(5672);
//设置账号 默认guest
factory.setUsername("lx");
//设置密码 默认guest
factory.setPassword("lx");
//设置虚拟机 默认 /
factory.setVirtualHost("/lx");
//获取连接对象
Connection connection = factory.newConnection();
//获取channel对象
Channel channel = connection.createChannel();
channel.exchangeDeclare("topic_exchange", BuiltinExchangeType.TOPIC,true);
//创建队列
//存在该队列则不会创建
channel.queueDeclare("topic_queue01",true,false,false,null);
channel.queueDeclare("topic_queue02",true,false,false,null);
//队列和交换机绑定
channel.queueBind("topic_queue01","topic_exchange","*.orange.*");
channel.queueBind("topic_queue02","topic_exchange","*.*.rabbit");
channel.queueBind("topic_queue02","topic_exchange","lazy.#");
//发送消息
String msg = "主题模式";
channel.basicPublish("topic_exchange","lazy.orange.lx",null,msg.getBytes());
//关闭资源
channel.close();
connection.close();
}
}
消費者 × 2 (1 つは下に書かれています):
package topic;
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* @program: qy-rabbitmq
* @description: 主题模式消费者
* @author:
* @create: 2023-07-12 18:15
**/
public class TopicConsumer01 {
public static void main(String[] args) throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/lx");
factory.setUsername("lx");
factory.setPassword("lx");
factory.setHost("192.168.232.166");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//接收队列中的消息
DefaultConsumer consumer = new DefaultConsumer(channel){
/**
*
* @param consumerTag: 消费者的标签
* @param envelope : 设置 拿到你的交换机 路由key等信息
* @param properties: 消息的属性对象
* @param body: 消息的内容
* @throws IOException
*/
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("接收的内容:"+new String(body));
}
};
/**
* String queue, 队列名
* boolean autoAck,是否自动确认。 当rabbitmq把消息发送给消费后,消费端自动确认消息。
* Consumer callback:回调。 当rabbitmq队列中存在消息 则触发该回调
*/
channel.basicConsume("topic_queue01",true,consumer);
}
}
Topic トピック モードは、Pub/Sub パブリッシングおよびサブスクリプション モードと Routing ルーティング モードの機能を実現できますが、Topic はルーティング キーを構成するときにワイルドカードを使用できるため、より柔軟です。