如何确认RabbitMq发送消息的可靠性
消息真的发出去了吗?
消息发送后,发送端不知道RabbitMQ是否真的收到了消息,如果RabbitM异常,那么消息丢失后,业务流程就停止,发生异常,所以我们需要使用RabbitMq发送端确认机制,确认消息发送
消息确认机制分为:单条同步确认机制,多条同步确认机制和异步确认机制
我们需要使用channel开启消息确认机制
//开启消息确认机制
channel.confirmSelect();
/**
单条和多条同步确认机制都是使用此方法,返回true或false
单条意味着每发出一条消息都会进行确认
多条意味着发出多条在进行确认,但是当消息出现异常时,只会返回false,无法确定是哪条消息出错
*/
channel.waitForConfirms()
/**
异步确认消息
会开启一个异步的线程进行确认消息,但是他不确定是返回多条消息的确认还是单条消息的确认是随机产生的
*/
ConfirmListener confirmListener = new ConfirmListener() {
/**
* 返回成功
* @param deliveryTag 消息的编号
* @param multiple true 确认多条,false 确认单条
* @throws IOException
*/
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
log.info("Ack, deliveryTag:{},multiple: {}", deliveryTag, multiple);
}
/**
* 返回失败
* @param deliveryTag 消息的编号
* @param multiple true 确认多条,false 确认单条
* @throws IOException
*/
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
log.info("Nack, deliveryTag:{},multiple: {}", deliveryTag, multiple);
}
};
channel.addConfirmListener(confirmListener);
综上所述,最好使用单条确认消息机制
消息真的被路由了吗?
消息发送后,发送端不知道消息是否被正确路由,若路由异常,消息会被丢弃,消息丢弃后,业务发生异常,所以我们需要使用RabbitMQ消息返回机制,确认消息被正确路由
注意消息确认代表消息是否被发送到消息段
而消息路由,是消息已经发送到消息端,确认消息是否被交换机发送到队列
/*-------使用消息返回机制---------*/
channel.addReturnListener(new ReturnListener() {
/**
* 只有在消息无法被路由时,才会调用此方法
* @param replyCode 返回编号
* @param replyText 返回消息
* @param exchange 交换机的名字
* @param routingKey 绑定的key
* @param properties
* @param body 要发送的消息
* @throws IOException
*/
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("Message Return:" +
"replyCode:{},replyText:{},exchange:{},routingKey:{},properties:{},body:{},",
replyCode,replyText,exchange,routingKey,properties,new String(body));
}
});
/**
在发送消息时,我们需要先开启消息返回机制
第三个参数代表是否开启消息返回机制
*/
channel.basicPublish("exchange.order.restaurant", "key.order", true,null, messageToSend.getBytes());
消费端处理异常怎么办
默认情况下,消费端接收消息时,消息会被自动确认(ACK),消费端消息处理异常时,发送端与消息中间件无法得知消息处理情况,所以需要使用RabbitMQ消息端确认机制,确认消息被正确处理
/**
首先需要将自动ACK关闭,改为手动ACK
第二个参数
*/
channel.basicConsume("queue.restaurant",false,deliverCallback, consumerTag -> {});
DeliverCallback deliverCallback = (consumerTag, message) -> {
/**
* 第一个参数表示消息的编号
* 第二个参数使用单条确认
*/
channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
}
队列爆满怎么办?
问题描述:在默认的情况下,消息进入队列,就会永远存在,直到被消费,如果要处理消息的业务挂掉或者宕机,就会导致队列的消息堆积,给RabbitMq造成压力, 这时就需要设置消息的过期时间,当消息进入队列一定时间内没有被消费则会直接删除消息。
为单条消息设置过期时间
在消息发送时,指定参数
AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("15000").build();
channel.basicPublish("exchange.order.restaurant", "key.restaurant", properties, messageToSend.getBytes());
为整个队列设置过期时间,在创建队列时,放入过期参数
Map<String,Object> args = new HashMap<>(16);
args.put("x-message-ttl",15000);
channel.queueDeclare(
"queue.restaurant",
true,
false,
false,
args
);
转移过期消息
当消息过期后,消息就会在队列中被删除,但是我们想要获得过期的消息怎么办呢? 这就需要设置死信队列, 死信队列就是一个简单的队列,用于存储过期消息。
//声明死信队列
channel.exchangeDeclare(
"exchange.dlx",
BuiltinExchangeType.TOPIC,
true,
false,
null
);
channel.queueDeclare(
"queue.dlx",
true,
false,
false,
null
);
channel.queueBind(
"queue.dlx",
"exchange.dlx",
"#"
);
//设置死信队列
Map<String,Object> args = new HashMap<>(16);
//第二个参数为死信队列的交换机名称
args.put("x-dead-letter-exchange","exchange.dlx");
channel.queueDeclare(
"queue.restaurant",
true,
false,
false,
args
);