rabbitTemplate.setReturnsCallback(returned -> {
//记录日志、发送邮件通知、落库定时任务扫描重发
});
}
}
测试的时候可以在发送消息时故意写错交换机、路由键的名称,然后就会回调到我们刚刚写的监听方法, cause 会给我们展示具体没有发到交换机的原因;returned 对象中包含了消息相关信息。
实际上据我了解一些企业并不会在这两个监听里面去做重发,为什么呢?成本太高了…首先 RabbitMQ 本身丢失的可能性就非常低,其次如果这里需要落库再用定时任务扫描重发还要开发一堆代码,分布式定时任务…再其次定时任务扫描肯定会增加消息延迟,不是很有必要。真实业务场景是记录一下日志就行了,方便问题回溯,顺便发个邮件给相关人员,如果真的极其罕见的是生产者弄丢消息,那么开发往数据库补数据就行了。
========================================================================
不开启持久化的情况下 RabbitMQ 重启之后所有队列和消息都会消失,所以我们创建队列时设置持久化,发送消息时再设置消息的持久化即可(设置 deliveryMode 为 2 就行了)。一般来说在实际业务中持久化是必须开的。
==================================================================
所谓消费端弄丢消息就是消费端执行业务代码报错了,那么该做的业务其实没有做。比如创建订单成功了,优惠券结算报错了,默认情况下 RabbitMQ 只要把消息推送到消费者就会认为消息已经被消费,就从队列中删除了,但是优惠券还没有结算,这样就相当于消息变相丢失了。这种情况还是很常见的,毕竟我们开发人员不能保证自己的代码不报错,这种问题一定得解决。 否则用户下了订单,优惠券没有扣减,你这个月的绩效估计是没了…
RabbitMQ 给我们提供了消费者应答(ack)机制,默认情况下这个机制是自动应答,只要消息推送到消费者就会自动 ack ,然后 RabbitMQ 删除队列中的消息。启用手动应答之后我们在消费端调用 API 手动 ack 确认之后,RabbitMQ 才会从队列删除这条消息。
首先在配置文件中开启手动 ack
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual #手动应答
然后在消费端代码中手动应答签收消息
@RabbitListener(queues = “queue”)
public void listen(String object, Message message, Channel channel) {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
log.info(“消费成功:{},消息内容:{}”, deliveryTag, object);
try {
/**
-
执行业务代码…
-
*/
channel.basicAck(deliveryTag, false);
} catch (IOException e) {
log.error(“签收失败”, e)