前面讲到了使用消息队列解耦时,生产端只需负责将消息投递到消息队列就完事了,那么必须要考虑的问题就是如何保证生产端将消息可靠的投递到了MQ中
AMQP协议以及RabbitMQ考虑到了这一问题,因此在设计时添加了Confirm消息确认机制和Return机制,首先先讲一下这两个机制
Confirm消息确认机制: 生产者向MQ投递完消息后,要求MQ返回一个应答,生产者异步接收该应答,用来确定该消息是否正常的发送到了Broker, 从而保障消息的可靠性投递
Return消息返回机制:该机制用于处理一些不可路由的消息。如果生产在发送消息时,发现当前的exchange不存在或者指定的路由key找不到时,生产者可以开启该模式来监听这种不可达的消息,以进行后续。(如果不开启的话,broker会自动删除该消息)
这里要注意的是,只要消息到达了MQ就换返回Confirm消息,接下来MQ再去判断能不能找到路由方式,找不到再返回Return消息
spring:
rabbitmq:
publisher-confirm-type: correlated
开启消息确认模式
RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack){
log.info(correlationData.toString() + "发送成功");
}else {
log.error(correlationData.toString() + "发送失败, 原因: " + cause);
}
}
};
其中重写的方法包含三个参数:
这里要注意,由于各种原因(如网络波动),生产端可能并没有收到confirm消息,因此不能将后续的补偿处理仅仅寄希望于在else内完成,else内做的补偿仅仅是在生产端收到confirm消息后nack的情况
rabbitTemplate.setConfirmCallback(confirmCallback());
rabbitTemplate.convertAndSend(exchange, routingkey, order, new CorrelationData(order.getId());
Return消息返回机制的实现与上面的Confirm消息确认机制的实现类似
spring:
rabbitmq:
publisher-returns: true
template:
mandatory: true
注意,mandatory一定要设置为true,否则找不到路由规则的消息会被broker直接抛弃
RabbitTemplate.ReturnCallback returnCallback = new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyTest, String exchange, String routingKey) {
log.error("消息{}路由失败,失败原因:{}",message.getMessageProperties().getMessageId(),replyTest);
}
};
rabbitTemplate.setReturnCallback(returnCallback);
rabbitTemplate.convertAndSend(exchange, routingkey, order, new CorrelationData(order.getId());
前面讲完了RabbitMQ自带的Confirm消息确认机制和Return机制,而实现消息可靠性投递的第一个方案就是利用该确认机制
实现思路如下:
该方案的缺点是在发送消息前,需要进行两次落库操作(修改数据库中的订单表和订单消息表),因此会对性能造成一定影响
实现思路如下:
该方案优点是在生产者端只需要入库一次,而将消息的入库操作独立到了Callback服务中去,提升了生产者端的性能。但是该方案实现较复杂,里面还有很多的细节值得考虑