下面的内容整理自:RabbitMQ实战指南.朱忠华
mandatory
参数源于channel.basicPublish
方法,函数签名如下:
void basicPublish(String exchange,
String routingKey,
boolean mandatory,
boolean immediate,
BasicProperties props,
byte[] body) throws IOException;
当
mandatory
参数设置为true
的时候,如果exchange
无法根据自身的type
以及routing key
找到一个符合条件的queue
,那么RabbitMQ
会调用Basic.Return
命令将消息返回给生产者;当mandatory
参数设置为false
的时候,出现上述情形,则消息直接被丢弃。
如果消息生产者在调用channel.basicPublish
方法的时候,设置mandatory
参数为true
,则可以通过调用channel.addReturnListener
来添加一个ReturnListener
,用以监听被RabbitMQ
送回来的消息。
public class MandatoryProducer {
public static final String EXCHANGE_MANDATORY = "exchange-mandatory";
public static void main(String[] args) {
// 1、创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.99.100");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("password");
// 2、获取连接、通道
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// 3、声明发送消息到哪个exchange
channel.exchangeDeclare(EXCHANGE_MANDATORY, BuiltinExchangeType.DIRECT);
// 4、设置ReturnListener
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode,
String replyText,
String exchange,
String routingKey,
AMQP.BasicProperties properties,
byte[] body) throws IOException {
System.out.println("Receive return message: " + new String(body));
System.out.println(String.format("replyCode: %d\nreplyText: %s\nexchange: %s\nroutingKey: %s\n",
replyCode, replyText, exchange, routingKey));
}
});
// 5、发送消息
int count = 1;
while (true) {
String message = "Mandatory message, count = " + count;
count++;
// 设置mandatory = true
channel.basicPublish(EXCHANGE_MANDATORY, "aaa", true, null, message.getBytes(StandardCharsets.UTF_8));
System.out.println("Send message: " + message);
Thread.sleep(2000);
}
} catch (TimeoutException | IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
"C:\Program Files\Java\jdk1.8.0_231\bin\java.exe" ...
Send message: Mandatory message, count = 1
Receive return message: Mandatory message, count = 1
replyCode: 312
replyText: NO_ROUTE
exchange: exchange-mandatory
routingKey: aaa
Send message: Mandatory message, count = 2
Receive return message: Mandatory message, count = 2
replyCode: 312
replyText: NO_ROUTE
exchange: exchange-mandatory
routingKey: aaa
备份交换机(Alternate Exchange
)的作用是可以将那些未被路由的消息存储在RabbitMQ
中,再在需要的时候处理这些消息。
可以通过在使用channel.exchangeDeclare
声明交换机的时候,指定alternate-exchange
参数来指定一个备份交换机。
// 参数
Map<String, Object> arguments = new HashMap<>();
arguments.put("alternate-exchange", "alter-exchange");
// 声明一个normal-exchange,指定他的备份交换机为alter-exchange
channel.exchangeDeclare("normal-exchange", BuiltinExchangeType.DIRECT, true, false, arguments);
// 声明备份交换机alter-exchange
channel.exchangeDeclare("alter-exchange", BuiltinExchangeType.FANOUT, true, false, null);
// 分别绑定queue
channel.queueDeclare("normal-queue", true, false, false, null);
channel.queueBind("normal-queue", "normal-exchange", "normal-key");
channel.queueDeclare("alter-queue", true, false, false, null);
channel.queueBind("alter-queue", "alter-exchange", "");
上面的代码声明了两个交换机normal-exchange
和alter-exchange
,其中指定了alter-exchange
为备份交换机。normal-queue
通过normal-key
绑定到normal-exchange
上,alter-queue
绑定到alter-exchange
上。
当消息无法被normal-exchange
路由的时候,将会被alter-exchange
路由到alter-queue
。
注意:如果备份交换机和
mandatory
参数一起使用,则mandatory
参数无效。
TTL
,Time To Live
的简称,过期时间。
RabbitMQ
可以对消息、队列设置过期时间TTL
。
有两种方法:
TTL
。 队列中所有的消息都有相同的TTL
。TTL
。 每条消息可以有自己的TTL
。当两种
TTL
都设置了,则消息的TTL
以两者中较小的那个数值为准。消息在队列中的时间一旦超过了设置的
TTL
,则消息将变成“死信”(Dead Message
)。对于第一种方式,一旦消息过期将会立即从队列中删除。对于第二种,消息过期了不一定立即从队列中删除,而是等到消息被消费的时候才做判断。
TTL
在使用channel.queueDeclare
声明队列的时候,通过x-message-ttl
参数设置消息的TTL
,单位为毫秒。
Map<String,Object> arguments = new HashMap<>();
arguments.put("x-message-ttl", 6000);
channel.queueDeclare("queue", true, false, false, arguments);
如果不设置TTL
,表示消息不会过期;如果TTL
设为0
,表示除非此时可以直接将消息投递给消费者,否则该消息会被立即丢弃。
在使用channel.basicPublish
方法发送消息的时候,加入expiration
参数设置消息的TTL
,单位为毫秒。
AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
builder.deliveryMode(2); // 持久化消息
builder.expiration("6000"); // 设置TTL = 6000 ms
channel.basicPublish("exchange", "routingKey", false, builder.build(), "message".getBytes());
在使用channel.queueDeclare
声明队列的时候,可以通过添加x-expires
参数来设置队列的过期时间,单位为毫秒。
队列的过期时间定义了队列被自动删除前处于未使用状态的时间。未使用表示队列上没有任何消费者,队列没有被重新声明,且在过期时间内也未调用过
Basic.Get
命令。
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-expires", 5 * 60 * 1000); // 5分钟
channel.queueDeclare("queue", false, false, false, arguments);
可以在使用channel.queueDeclare
声明队列的时候,通过参数x-dead-letter-exchange
设置死信交换机。
死信交换机(Dead-Letter-Exchange
,简称DLX
)和一般的交换机没什么区别。绑定到DLX
上面的队列叫做死信队列。
当消息变为死信,可以发送到死信队列上进行处理。
消息变为死信,一般有以下几种情况:
Basic.Reject
/Basic.Nack
),并且设置requeue
为false
;// 声明普通交换机
channel.exchangeDeclare("normal-exchange", BuiltinExchangeType.DIRECT);
// 声明死信交换机
channel.exchangeDeclare("dlx-exchange", BuiltinExchangeType.FANOUT);
// 队列参数
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-message-ttl", 10000);
arguments.put("x-dead-letter-exchange", "dlx-exchange");
// 声明普通队列并绑定到normal-exchange
channel.queueDeclare("normal-queue", true, false, false, arguments);
channel.queueBind("normal-queue", "normal-exchange", "routingKey");
// 声明死信队列并绑定到dlx-exchange
channel.queueDeclare("dlx-queue", true, false, false, null);
channel.queueBind("dlx-queue", "dlx-exchange", "");
延迟队列用于存储延迟消息。
这些消息被发送之后,不希望立刻被消息者消费,而是希望等待一定时间之后再被消费。
RabbitMQ
本身没有提供延迟队列的功能,但是可以通过死信交换机DLX
和过期时间TTL
来实现。
实现原理:消费者不去订阅普通队列,而是去订阅死信队列,当消息在普通队列过期之后,会被发送到相应的死信队列中,消息的过期时间就是延迟时间。
优先级队列可以处理消息的优先级,高优先级的消息将会优先被消费者消费。
在调用channel.queueDeclare
声明队列的时候,可以通过蛇者x-max-priority
来设置队列允许的最高优先级的数值,默认最低为0
。
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-max-priority", 10);
channel.queueDeclare("priority-queue", true, false, false, arguments);
在发送的消息中设置消息的优先级:
AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
builder.priority(5); // 设置消息的优先级
channel.basicPublish("priority-exchange", "routingKey", false, builder.build(), "message".getBytes());