交换机
RabbitMQ交换机,真正接收生产者消息的组件。来自生产者的消息不是直接发送给队列,生产者只能将消息发送给交换机,在由交换机的配置属性将消息发送到指定的队列中或者将没有找到指定队列将消息丢弃。
Exchange(交换机)直接与Channel(信道)连接,接收来自于消息生产者产生的数据,在由Exchange将消息路由到一个或多个Queue中(或者丢弃)。Exchange并不存储消息。

声明交换机
在我们没有进行手动创建交换机的时候,RabbitMQ会使用中间件自带的默认交换机,当然我们是可以进行手动创建交换机的,因为我们可以为自己创建的交换机设置一些我们需要的属性,便于交换机能够根据我们设置的规则,更好的进行消息传递。
方法代码
/*
* 声明交换机
* @param1 exchange 交换机名称
* @param2 type 交换机类型
* @param3 durable 是否持久化
* @param4 autoDelete 是否自动删除
* @param5 internal 当前Exchange是否用于RabbitMQ内部使用,默认false
* @param6 arguments 扩展参数,用户扩展AMQP定制化协议
*
* @return AMQP.Exchange.DeclareOk 用来标识成功声明一个交换机
* 打印信息: #method<exchange.declare-ok>()
*/
AMQP.Exchange.DeclareOk declareOk = channel.exchangeDeclare("fanout_exchange",
BuiltinExchangeType.FANOUT, false, false, false, null);
System.out.println(declareOk);
上面声明交换机的方法,是有多个重载方法的,这些重载的方法都是由上面的这个方法种缺省了某些参数构成的。
声明方法参数介绍
- exchange 交换机名称。
- type 交换机类型,常用类型有fanout(扇出),direct(直接),topic(主题),headers(标题)。
- durable 是否持久化,true表示持久化,false表示非持久化,持久化可以将交换机存盘,在服务器重启的时候不会丢失相关数据。
- autoDelete 是否自动删除,true表示自动删除,false表示不自动删除。自动删除是指和该交换机绑定的队列或其它交换机解除绑定关系,而不是交换机与生产者断开时RabbitMQ将此交换机删除。
- internal 是否将此交换机设置为内置交换机,true是表示为内置交换机,false表示非内置交换机,默认为false,当交换机被设置为内置时,此交换机只能用于RabbitMQ内部使用,生产者无法将消息发送到此交换机,只能通过交换机路由到此交换机。
- arguments 扩展参数,用户扩展AMQP定制化协议,比如:alternate-exchange(备份交换机,表示该交换机为备份交换机,备份交换器是为了实现没有路由到队列的消息,声明交换机的时候添加属性alternate-exchange,声明一个备用交换机,一般声明为fanout类型,这样交换机收到路由不到队列的消息就会发送到备用交换机绑定的队列中。),后面会将参配置的参数列出。
交换机常用方法
/*
* 声明交换机
* @param1 exchange 交换机名称
* @param2 type 交换机类型
* @param3 durable 是否持久化
* @param4 autoDelete 是否自动删除
* @param5 internal 当前Exchange是否用于RabbitMQ内部使用,默认false
* @param6 arguments 扩展参数,用户扩展AMQP定制化协议
*
* @return AMQP.Exchange.DeclareOk 用来标识成功声明一个交换机
*/
AMQP.Exchange.DeclareOk declareOk = channel.exchangeDeclare("fanout_exchange",
BuiltinExchangeType.FANOUT, false, true, false, null);
/*
* 声明交换机,该方法不会等待RabbitMQ返回声明是否成功的状态,
* 就自认为声明成功了,在声明的过程种可能因为某种原因交换机声明失败,
* 而此方法并不知道,可能导致生产者无法将消息发送到此交换机上。
*
* @param1 exchange 交换机名称
* @param2 type 交换机类型
* @param3 durable 是否持久化
* @param4 autoDelete 是否自动删除
* @param5 internal 当前Exchange是否用于RabbitMQ内部使用,默认false
* @param6 arguments 扩展参数,用户扩展AMQP定制化协议
*
* @return void
*/
channel.exchangeDeclareNoWait("fanout_exchange",
BuiltinExchangeType.FANOUT, false, true, false, null);
/*
* 根据交换机名称插叙该交换机是否存在
* @param1 exchange 交换机名称
* @return AMQP.Exchange.DeclareOk 用来标识一个交换机是否存在。
* 存在返回 exchange.declare-ok,
* 不存在返回 404,同时Channel也会被关闭。
*/
AMQP.Exchange.DeclareOk declareOk1 = channel.exchangeDeclarePassive("fanout_exchange");
/*
* 删除交换机
*
* @param1 exchange 交换机名称
*/
AMQP.Exchange.DeleteOk deleteOk = channel.exchangeDelete("fanout_exchange");
/*
* 删除交换机没有被使用的交换机
*
* @param1 exchange 交换机名称
* @param2 ifUnused 是否删除未被使用的交换机,
* true表示只有在交换机没有被使用的时候才会被删除,
* false表示无论如何都要删掉。
*/
AMQP.Exchange.DeleteOk deleteOk1 = channel.exchangeDelete("fanout_exchange",true);
/*
* 删除交换机没有被使用的交换机,该方法表示不会等RabbitMQ给是否删除成功的消息,
* 就认为是已经删除了,这个方法可能会因为某种原因导致交换机没有被删除,而以为被删除了。
*
* @param1 exchange 交换机名称
* @param2 ifUnused 是否删除未被使用的交换机,
* true表示只有在交换机没有被使用的时候才会被删除,
* false表示无论如何都要删掉。
*/
channel.exchangeDeleteNoWait("fanout_exchange",true);
到此我们已经声明好了交换机,接下来就是开始使用各种类型的交换机了。
分类
RabbitMQ交换机分为fanout(扇出),direct(直接),topic(主题),headers(标题)四种类型,每种交换机类型都对应着不同的路由规则,根据不同的路由规则,交换机会将消息路由到不同的队列中。
在创建交换机和队列之后,是要将他们绑定起来的,所以要正常发送消息需要以下的过程。
创建交换机 ➡ 创建队列 ➡ 绑定关系 ➡ 发送消息
Fanout:(扇出,扇形)
扇出型交换机路由规则非常简单,它不处理路由键,该交换机会把消息路由到所有与本交换机绑定的队列上。
如下图:交换机会把接收到的消息发送到所有的队列中。
生产者代码
package rabbitmq.ced.exchange;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import rabbitmq.ced.utils.RabbitMqUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 扇出型交换机
* 将三个队列与交换机绑定,然后向这个交换机发送一条消息,
* 三个与之绑定的队列都可以收到这个消息。
* <p>
* 创建生产者
*
* @author 崔二旦
* @since now
*/
public class FanoutExchangeProducer {
public static void main(String[] args) {
try {
Channel channel;
// 交换机名称
String exchangeName = "fanout_exchange";
// 准备要被发送的消息内容
String message = "崔二旦";
// 队列名称,三个队列
String queueName1 = "hello1";
String queueName2 = "hello2";
String queueName3 = "hello3";
//路由键
String routingKey = "fanout_binding";
channel = RabbitMqUtils.getChannel();
// 判断交换机是否参在
boolean isExist = exchangeIsExist(channel, exchangeName);
if (!isExist) {
if (!channel.isOpen()) {
channel = RabbitMqUtils.getChannel();
// 创建交换机
boolean declareOk = exchangeDeclare(channel, exchangeName);
if (declareOk) {
// 创建三个队列
boolean queueDeclareOk1 = queueDeclare(channel, queueName1);
boolean queueDeclareOk2 = queueDeclare(channel, queueName2);
boolean queueDeclareOk3 = queueDeclare(channel, queueName3);
if (queueDeclareOk1 && queueDeclareOk2 && queueDeclareOk3) {
/*
* 队列绑定,是将队列绑定到交换机上
* @param1 queue 队列名称
* @param2 exchange 交换器名称
* @param3 routingKey 路由key
*/
channel.queueBind(queueName1, exchangeName, routingKey);
channel.queueBind(queueName2, exchangeName, routingKey);
channel.queueBind(queueName3, exchangeName, routingKey);
//发送消息
channel.basicPublish("fanout_exchange", routingKey,
null, message.getBytes(StandardCharsets.UTF_8));
System.out.println("消息发送成功");
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("消息发送异常");
}
}
/**
* 判断队列是否存在
*
* @param channel 信道
* @param exchangeName 队列名称
* @return boolean
*/
public static boolean exchangeIsExist(Channel channel, String exchangeName) {
boolean isExist = true;
try {
channel.exchangeDeclarePassive(exchangeName);
} catch (IOException ioe) {
isExist = false;
}
return isExist;
}
/**
* 创建交换机
*
* @param channel 信道
* @param exchangeName 队列名称
* @return boolean
*/
public static boolean exchangeDeclare(Channel channel, String exchangeName) {
boolean status = true;
try {
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT,
false, false, false, null);
} catch (IOException ioe) {
status = false;
}
return status;
}
/**
* 创建队列
*
* @param channel 信道
* @param queueName 队列名称
* @return boolean
*/
public static boolean queueDeclare(Channel channel, String queueName) {
boolean status = true;
try {
channel.queueDeclare(queueName, false, false, true, null);
} catch (IOException ioe) {
ioe.printStackTrace();
status = false;
}
return status;
}
/**
* 创建队列的工具方法
* @return channel 信道
* @throws Exception 异常
*/
public static Channel getChannel() throws Exception {
// 1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2. 设置连接属性
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
// 3. 从连接工厂中获取连接
Connection connection = connectionFactory.newConnection("生产者");
// 3. 在连接中创建信道,RabbitMQ中的所有操作都是在信道中完成的
Channel channel = connection.createChannel();
return channel;
}
}
上面的例子看起来有点麻烦,不便于理解,所以可以看下面的代码。
简单的生产者代码
package rabbitmq.ced.exchange;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.nio.charset.StandardCharsets;
/**
* 扇出型交换机
* 将三个队列与交换机绑定,然后向这个交换机发送一条消息,
* 三个与之绑定的队列都可以收到这个消息。
* <p>
* 创建生产者
*
* @author 崔二旦
* @since now
*/
public class FanoutSimpleExchangeProducer {
public static void main(String[] args) {
// 1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2. 设置连接属性
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection = null;
Channel channel = null;
try {
// 3. 从连接工厂中获取连接
connection = connectionFactory.newConnection("生产者");
// 3. 在连接中创建信道,RabbitMQ中的所有操作都是在信道中完成的
channel = connection.createChannel();
// 交换机名称
String exchangeName = "fanout_exchange";
// 准备要被发送的消息内容
String message = "崔二旦";
// 队列名称,三个队列
String queueName1 = "hello1";
String queueName2 = "hello2";
String queueName3 = "hello3";
//路由键
String routingKey = "fanout_binding";
// 创建交换机
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT,
false, false, false, null);
// 创建三个队列
channel.queueDeclare(queueName1, false, false, true, null);
channel.queueDeclare(queueName2, false, false, true, null);
channel.queueDeclare(queueName3, false, false, true, null);
//绑定
channel.queueBind(queueName1, exchangeName, routingKey);
channel.queueBind(queueName2, exchangeName, routingKey);
channel.queueBind(queueName3, exchangeName, routingKey);
//发送消息
channel.basicPublish("fanout_exchange", routingKey,
null, message.getBytes(StandardCharsets.UTF_8));
System.out.println("消息发送成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("消息发送异常");
}
}
}
RabbitMQ管理界面展示出来的关系
Direct:(直接)
直接型交换机需要和Routing Key 打交道了,在交换机接收到带有Routing Key 的消息后,会与交换机和队列之间设置的Binding路由规则进行匹配,当Routing Key与Binding Key完全匹配后,会将消息路由到可以匹配上的队列中。
如下图
带有Routing Key="weixin"的消息被发送到交换机后,交换机与第二个队列中设置的路由规则为"weixin"的队列匹配上了,所以交换机将消息路由到了第二个的队列中。
生产者代码
package rabbitmq.ced.exchange;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.nio.charset.StandardCharsets;
/**
* 交换机
* 直接型交换机
* 创建生产者
*
* @author 崔二旦
* @since now
*/
public class DirectExchangeProducer {
public static void main(String[] args) {
// 1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2. 设置连接属性
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection ;
Channel channel ;
try {
// 3. 从连接工厂中获取连接
connection = connectionFactory.newConnection("生产者");
// 3. 在连接中创建信道,RabbitMQ中的所有操作都是在信道中完成的
channel = connection.createChannel();
// 交换机名称
String exchangeName = "direct_exchange";
// 准备要被发送的消息内容
String message = "崔二旦";
// 队列名称,三个队列
String queueName1 = "queue_name1";
String queueName2 = "queue_name2";
String queueName3 = "queue_name3";
//绑定Key
String bindingKey1 = "qq";
String bindingKey2 = "weixin";
String bindingKey3 = "email";
//routing key
//可以匹配上
String routingKey1 = "qq";
//可以匹配上
String routingKey2 = "email";
//匹配不上
String routingKey3 = "nb";
// 创建交换机
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT,
false, false, false, null);
// 创建三个队列
channel.queueDeclare(queueName1, false, false, true, null);
channel.queueDeclare(queueName2, false, false, true, null);
channel.queueDeclare(queueName3, false, false, true, null);
//绑定
channel.queueBind(queueName1, exchangeName, bindingKey1);
channel.queueBind(queueName2, exchangeName, bindingKey2);
channel.queueBind(queueName3, exchangeName, bindingKey3);
//发送消息
channel.basicPublish(exchangeName, routingKey1,
null, message.getBytes(StandardCharsets.UTF_8));
channel.basicPublish(exchangeName, routingKey2,
null, message.getBytes(StandardCharsets.UTF_8));
channel.basicPublish(exchangeName, routingKey3,
null, message.getBytes(StandardCharsets.UTF_8));
System.out.println("消息发送成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("消息发送异常");
}
}
}
RabbitMQ管理界面展示出来的关系
对于上面展示出来的效果分析
解释
有三个队列与交换机绑定,Binding Key 分别是qq、weixin、email,当交换机接收到消息之后,会取出消息中携带的Routing Key 进行完全匹配,可以匹配上就会把消息发送到对应的队列中,交换机会把没有匹配上的丢弃。
生产者发送三个消息,其中携带的Routinf Key 分别是qq、email、nb,发送到交换机之后,交换机会和和自己有绑定关系中的Binding Key 进行完全匹配,根据上图,最终只有Routing Key 为qq、email可以匹配上,消息也发到了对应的队列中,而 Routing Key 为nb 的没有匹配上,被丢弃,而Binding Key为weixin的没有消息获取。
所以,queue_name1有一条消息,queue_name2有零条消息,queue_name3有一条消息。
Topic:(主题)
主题型交换机是对Direct(直接)型交换机的升级,当直接型交换机的严格匹配不能满足需求的时候,就可以使用Topic(主题)型交换机,主题型交换机是可以在Routing Key 与Binding Key 匹配时进行模糊匹配的。符号“#”匹配一个或多个词,符号“*”只能匹配一个词。
如下图
Routing Key 的值为"love.ced.nb"的消息发送到交换机,交换机会根据Binding Key中的规则进行模糊匹配,所以第一个和第三个是满足匹配条件的,所以交换机将消息路由到了这两个队列中。
生产者代码
package rabbitmq.ced.exchange;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.nio.charset.StandardCharsets;
/**
* 交换机
* 主题型交换机
* 创建生产者
*
* @author 崔二旦
* @since now
*/
public class TopicExchangeProducer {
public static void main(String[] args) {
// 1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2. 设置连接属性
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection ;
Channel channel ;
try {
// 3. 从连接工厂中获取连接
connection = connectionFactory.newConnection("生产者");
// 3. 在连接中创建信道,RabbitMQ中的所有操作都是在信道中完成的
channel = connection.createChannel();
// 交换机名称
String exchangeName = "topic_exchange";
// 准备要被发送的消息内容
String message = "崔二旦";
// 队列名称,三个队列
String queueName1 = "topic_queue_name1";
String queueName2 = "topic_queue_name2";
String queueName3 = "topic_queue_name3";
//绑定Key,模糊匹配
String bindingKey1 = "*.ced.*";
String bindingKey2 = "*.clr.nb";
String bindingKey3 = "#.nb";
//routing key
//可以匹配上
String routingKey1 = "love.ced.nb";
//可以匹配上
String routingKey2 = "love.ced.znb";
//匹配不上
String routingKey3 = "love.clr.znb";
// 创建交换机
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC,
false, false, false, null);
// 创建三个队列
channel.queueDeclare(queueName1, false, false, true, null);
channel.queueDeclare(queueName2, false, false, true, null);
channel.queueDeclare(queueName3, false, false, true, null);
//绑定
channel.queueBind(queueName1, exchangeName, bindingKey1);
channel.queueBind(queueName2, exchangeName, bindingKey2);
channel.queueBind(queueName3, exchangeName, bindingKey3);
//发送消息
channel.basicPublish(exchangeName, routingKey1,
null, message.getBytes(StandardCharsets.UTF_8));
channel.basicPublish(exchangeName, routingKey2,
null, message.getBytes(StandardCharsets.UTF_8));
channel.basicPublish(exchangeName, routingKey3,
null, message.getBytes(StandardCharsets.UTF_8));
System.out.println("消息发送成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("消息发送异常");
}
}
}
RabbitMQ管理界面展示出来的关系
对于上面展示出来的效果分析
解释
因为主题模式是可以对Routing Key 与Binding Key 中的值进行模糊匹配的,符号“#”匹配一个或多个词,符号“*”只能匹配一个词。根据匹配符号的规则,可以得出结论,第一个消息可以被路由到第一个和第三个队列中,第二个消息只能被路由到第一个队列中,第三个消息则不会被路由到任何一个队列中。所以第一个队列中有2个消息,第二个队列中0个消息,第三个队列中有1个消息。
主题模式的速度比fanout和direct要慢,因为是模糊匹配,所以使用时要考虑以下。
Headers:(标题)
标题型交换机不对路由键进行匹配,而是根据消息内容中的headers属性进行匹配,在对Exchange与Queue进行绑定时设置一组键值对,当消息发送到交换机后,RabbitMQ会取出该消息的headers属性信息,对其中的属性键值对与绑定的键值对进行匹配,交换机会将消息路由到匹配成功的队列中。标题型交换机设置的键值对可以是任意类型。而其它类型交换机的路由键则是字符串类型。
如下图
带有"ced":"nb" headers属性的消息发送到交换机后,交换机根据与每个队列设置的键值对进行匹配,第一个和第三个可以匹配成功,所以交换机将消息路由到这两个队列中。
生产者代码
package rabbitmq.ced.exchange;
import com.rabbitmq.client.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* 交换机
* 标题型交换机
* 创建生产者
*
* @author 崔二旦
* @since now
*/
public class HeadersExchangeProducer {
public static void main(String[] args) {
// 1.创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
// 2. 设置连接属性
connectionFactory.setHost("localhost");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
Connection connection;
Channel channel;
try {
// 3. 从连接工厂中获取连接
connection = connectionFactory.newConnection("生产者");
// 3. 在连接中创建信道,RabbitMQ中的所有操作都是在信道中完成的
channel = connection.createChannel();
// 交换机名称
String exchangeName = "headers_exchange";
// 准备要被发送的消息内容
String message = "崔二旦";
// 队列名称,三个队列
String queueName1 = "headers_queue_name1";
String queueName2 = "headers_queue_name2";
String queueName3 = "headers_queue_name3";
//设置第一个队列的header信息
Map<String, Object> queueHeader1 = new HashMap<>(3);
queueHeader1.put("x-match", "any");
queueHeader1.put("ced", "nb");
queueHeader1.put("clr", "nb");
//设置第二个队列的header信息
Map<String, Object> queueHeader2 = new HashMap<>(2);
queueHeader2.put("x-match", "any");
queueHeader2.put("clr", "nb");
//设置第三个队列的header信息
Map<String, Object> queueHeader3 = new HashMap<>(2);
queueHeader3.put("x-match", "all");
queueHeader3.put("ced", "nb");
queueHeader3.put("clr", "nb");
//设置消息的header信息
Map<String, Object> messageHeader = new HashMap<>(2);
messageHeader.put("ced", "nb");
AMQP.BasicProperties.Builder properties = new AMQP.BasicProperties().
builder().headers(messageHeader);
// 创建交换机
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.HEADERS,
false, false, false, null);
// 创建三个队列
channel.queueDeclare(queueName1, false, false, true, null);
channel.queueDeclare(queueName2, false, false, true, null);
channel.queueDeclare(queueName3, false, false, true, null);
//绑定
channel.queueBind(queueName1, exchangeName, "", queueHeader1);
channel.queueBind(queueName2, exchangeName, "", queueHeader2);
channel.queueBind(queueName3, exchangeName, "", queueHeader3);
//发送消息
channel.basicPublish(exchangeName, "", properties.build(),
message.getBytes(StandardCharsets.UTF_8));
System.out.println("消息发送成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("消息发送异常");
}
}
}
RabbitMQ管理界面展示出来的关系
对于上面展示出来的效果分析
解释
标题模型中,消息是根据properties即请求头中key-value来匹配的,消费方指定的headers中必须包含一个"x-match"的键。
键"x-match"的值有2个:all和any。
- all:表示消费方指定的所有key-value都必须在消息header中出现并匹配。
- any:表示消费方指定的key-value至少有一个在消息header中出现并匹配即可。
所以根据all和any可以推断,消息不会被路由到第三个队列中,在第二个队列中的x-match是any,但键值是"clr":"nb",和消息的不匹配,所以消息不会被路由到第二个队列中,第一个队列中的x-match是any,而且还有"ced":"nb"的键值对,消息只能被路由到第一个队列中。所以第一个队列1个消息,第二个队列0个消息,第三个队列0个消息。
总结
到此RabbitMQ交换机的部分已经介绍完毕,在原则交换机类型的时候,根据需要选择合适的交换机类型。不推荐主题类型和标题类型,一个有点慢,一个太麻烦了。
附录
arguments扩展参数