前情回顾
上一章,我们说了如果不指定exchange就会默认的exchange。而默认的exchange的交换类型是direct。也就是说只有RoutingKey和消息队列的名称完全一致的情况下,exchange(交换机)才会将消息转发到消息队列中。
这一篇我们说一下topic消息模式。
topic exchange
topic是可以根据RoutingKey的规则,将一个消息转发到多个消息队列中。它的语法规则如下:
- 符号: “#” 匹配一个或多个词
- 符号: “*” 匹配一个词
- 符号: “.” 表示分隔一个词。
例如:
“log.#” 能够匹配到 log.info.blog 和log.err
“log.*” 能够匹配到 log.err 但是不能匹配到 log.info.blog
如下图: 官网图示
根据 这个图我们可以看到一个交换机可以和多个消息队列绑定,而他们绑定的关系就是RoutingKey,每一个RoutingKey都是不一致的。 我们在发送消息的时候是通过指定exchange和RoutingKey来确定发送到哪一个消息队列中。
Routingkey 为usa.news 的消息能过exchange交换机转发到了usa.#这个消息队列和#.news消息队列中。(usa.#是将消息队列和exchange绑定的Routingkey。) 这个图就展示了一个消息是可以发送到多个消息队列中的。
代码示列
生产者
package com.bfxy.rabbitmq.api.exchange.topic;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Producer4TopicExchange {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2 创建Connection
Connection connection = connectionFactory.newConnection();
//3 创建Channel
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_topic_exchange";
String routingKey1 = "user.save";
String routingKey2 = "user.update";
String routingKey3 = "user.delete.abc";
//5 发送
String msg = "Hello World RabbitMQ 4 Topic Exchange Message ...";
channel.basicPublish(exchangeName, routingKey1 , null , msg.getBytes());
channel.basicPublish(exchangeName, routingKey2 , null , msg.getBytes());
channel.basicPublish(exchangeName, routingKey3 , null , msg.getBytes());
channel.close();
connection.close();
}
}
消费者
package com.bfxy.rabbitmq.api.exchange.topic;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
public class Consumer4TopicExchange {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory() ;
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_topic_exchange";
String exchangeType = "topic";
String queueName = "test_topic_queue";
//String routingKey = "user.*";
String routingKey = "user.*";
// 1 声明交换机
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
// 2 声明队列
channel.queueDeclare(queueName, false, false, false, null);
// 3 建立交换机和队列的绑定关系:
channel.queueBind(queueName, exchangeName, routingKey);
//durable 是否持久化消息
QueueingConsumer consumer = new QueueingConsumer(channel);
//参数:队列名称、是否自动ACK、Consumer
channel.basicConsume(queueName, true, consumer);
//循环获取消息
while(true){
//获取消息,如果没有消息,这一步将会一直阻塞
Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg);
}
}
}
从代码中我们可以看到所有的消息都是通过channel发送的。在消息者创建exchange的时候,我们指定RoutingKey是“user.*”可以将其替换成“user.#”查看一下结果。
在这里要注意一点的是,Exchange是可以绑定多个Routingkey的, 如果想让RoutingKey失交需要手动解绑。
在RabbitMQ的管理平台上, 打开指定的消息队列。点击unbind.
API解绑
Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey) throws IOException;
Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey, Map<String, Object> arguments) throws IOException;
可以通过上面的API进行解除绑定。
参考文章
解除绑定部分