echo編輯整理,歡迎轉載,轉載請聲明文章來源。歡迎添加echo微信(微信號:t2421499075) 交流學習。
上兩篇文章中,我們使用Direct Exchange和Fanout Exchange類型推送了消息,但是我們發現這兩種類型在某些情況下並不適合,比如:我們MQ中有一百個不同隊列訂閱了我們的某個生產者,但是在某一次的時候,我們生產者只希望其中一部分對了能接受消息,那麼我們的Fanout Exchange直接廣播就會造成信息資源浪費,如果我們選擇Direct Exchange的時候,那我們要發送給對應的幾十個隊列就會要不斷設置,重複工作很多,也很容易出錯。但是要是能夠根據我們的隊列RoutingKey來模糊匹配是不是很我們的問題就完美解決了啊。本文就是講解這種模式
使用Topic Exchange根據規則推送一條消息
- 準備工作 既然我們要根據規則推送給某一批隊列,我們要先建立一批隊列,指定路由器類型爲Topic Exchange,然後我們使用規則定義routing key。我們這裏準備了三個隊列
並通過routing key進行綁定到topic類型的Exchange上
routing key作用解析
routing key的命名是我們Topic Exchange類型的最關鍵部分,在推送消息的時候,如果是使用topic類型的Exchange可以直接不指定隊列,所以這個時候routing key就成爲了我們推送消息的關鍵。爲了消息能夠更好的推送接受,在這個地方我們肯定要規範的定義routing kye的名稱。在我們這裏,我們看了總共有三個routing key
- com.echo.* :當推送消息時routing key爲com.echo.任意字符的時候,隊列queue_topic1對應的隊列一定能接受到消息
- com.echo.level2 :當推送消息時routing key爲com.echo.level2,隊列queue_topic2對應的隊列纔會接受到消息
#
:當推送消息時routing key爲任意值,隊列queue_topic2對應的隊列都能接受到消息
其中 * 匹配任意一個單詞,# 匹配零個或者多個單詞。
代碼案例
package com.example.demo;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* @author echo
* @date 2021-01-14 14:35
*/
public class TopicProductTest {
private static final String EXCHANGE_NAME = "exchange_topic";
private static final String ROUTING_KEY = "com.echo.level2";
private static final String IP_ADDRESS = "192.168.230.131";
private static final int PORT = 5672;
public static void main(String[] args) throws IOException, TimeoutException {
// 創建連接工廠
ConnectionFactory factory = new ConnectionFactory();
// 設置RabbitMQ的鏈接參數
factory.setHost(IP_ADDRESS);
factory.setPort(PORT);
factory.setUsername("echo");
factory.setPassword("123456");
// 和RabbitMQ建立一個鏈接
Connection connection = factory.newConnection();
// 創建一個頻道
Channel channel = connection.createChannel();
// 創建一個 type="direct" 、持久化的、非自動刪除的交換器
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC, true, false, null);
// 發送一條持久化的消息: topic hello world !
String message = "topic hello world !";
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());
// 關閉資源
channel.close();
connection.close();
}
}
按照上面routing key的作用,我們這個實例推送的消息,對應的三個隊列應該都會受到消息,我們運行了看看結果
最終結果和我們的作用域的描述符合
- 我們還可以使用消費者來驗證一下 消費者代碼
package com.example.demo;
import com.rabbitmq.client.*;
import lombok.SneakyThrows;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author tang.sl
* @date 2021-01-14 15:05
*/
public class TopicConsumerTest {
private static final String EXCHANGE_NAME = "exchange_topic";
private static final String QUEUE_NAME = "queue_topic1";
private static final String IP_ADDRESS = "192.168.230.131";
private static final int PORT = 5672;
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
// 設置RabbitMQ的鏈接參數
factory.setUsername("echo");
factory.setPassword("123456");
factory.setPort(PORT);
factory.setHost(IP_ADDRESS);
// 和RabbitMQ建立一個鏈接
Connection connection = factory.newConnection();
final Channel channel = connection.createChannel();
//聲明交換機 Fanout模式
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC, true, false, null);
//進行綁定,指定消費那個隊列
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "", null);
Consumer consumer = new DefaultConsumer(channel) {
@SneakyThrows
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
System.out.println("recv message: " + new String(body));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
channel.basicConsume(QUEUE_NAME, consumer);
//等待回調函數執行完畢之後 關閉資源
TimeUnit.SECONDS.sleep(5);
channel.close();
connection.close();
}
}
運行之後結果一致
總結
topic能夠對指定的一系列或者說一堆的隊列發送消息,關鍵就是靠routing key的*和#這兩個通配符的匹配作用