消息丢失怎么办
3种情况,mq消息丢失做集群开启持久化durable
如何可靠消费 开启ack重试机制一般重试3-5次,不行的话就把这个消息打到死信队列,死信队列不行再打到重定向队列,重定向不行就报警存到数据库或者缓存中报警人工进行处理.
第一个问题:如和保证消费者能够一定收到消息:
- 做消息的持久队列(Queue)
- rabbitmq做集群
第二个问题:如果保证消息的可靠生产(rabbitmq全部挂了)
- 做消息的备份(冗余)状态0,当rabbitmq服务器好的时候,那么接下类开一个定时器(quartz)间隔一段时间(时间一般通过业务来决定)。
- 状态如何修改呢?会通过rabbitmq的ack机制来完成confirm,在生产者这边吧消息的状态修改1。
第三种情况:消费报异常了,造成消费无法确认。
- 会造成死循环
- 手动ACK + try/catch
- 如果出现错误做法把消息放入死信队列
- 如果死信队列还报错,会死信队列绑定一个重定向队列,在发送消息去消费。
- 如果订单想队列还报错。那么只能把消息打入存储库(mysql,redis,mongodb等) +
- 报警(发短信) + 人工介入
- 重试机制,但是会造成消息丢失
第四问题:幂等性问题
- 因为用户可能重复提交订单
- 把订单编号设置redis的key
- 如果key存在,订单已经生成了
- 如果不存在,订单在生产中。
在这里插入代码片
/**
* 消息生产者
*/
@Component
public class Product implements RabbitTemplate.ConfirmCallback{
private RabbitTemplate rabbitTemplate;
@Autowired
public Product(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
rabbitTemplate.setConfirmCallback(this);
}
/**
* 生产者发送消息
* @param msg
*/
public void sendMsg(String msg) {
System.out.println("生产者发送消息 : " + msg);
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
System.out.println("生产者生成确认接到消息的 UUID: " + correlationData.getId());
this.rabbitTemplate.convertAndSend("topicExchange", "topic.itcast", msg, correlationData);
}
/**
* 回调的方法
* @param correlationData
* @param ack
* @param cause
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("确认回调的UUID: " + correlationData.getId() + ",是否收到消息=" + ack + " , 原因:" + cause);
}
}
@Component
@RabbitListener(queues = "itcast")
public class Consumer {
@RabbitHandler
public void con1(String msg, Channel channel, Message message) throws IOException {
try {
System.out.println("itcast 消费者: "+msg);
//消息确认接收
/**
* 参数一:message.getMessageProperties().getDeliveryTag() 当前消息索引号
* 参数二:是否通知全部queue
*/
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} catch (Exception e) {
//丢弃这条消息
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,false);
e.printStackTrace();
}
}
}
个人理解:
生产者发消息之后再confirm方法里面判断根据返回的布尔值进行判断,如果是false你就将这条数据存到id数据库中,给个状态status 为0,然后写个定时任务去查状态为0的消息表中的数据,可以重新进行投递,也可以直接调接口要干啥,消费者这边只需要调用channel.basciAck 告知mq 丢弃这条消息不用发了,如果发现被捕获了也是丢弃消息重新发送