解决mq消息可靠性投递,从三个方面解决这个问题
1、消息生产者
2、消息在队列中
3、消息消费者
如图:
1、发送者丢数据
事务模式
// 将channel设置成事务模式
channel.tsSelect();
// 提交事务
channel.txCommit();
// 事务回滚
channel.txRooback();
// transaction 模式,开启事务, 然后发送消息,如果中途发送出现异常,
// 事务回滚,如果没有异常,事务提交
// 这种方式会导致吞吐量下降。 所以线上用下一种方式居多
Confirm模式
confirm 模式,每个消息指派唯一id如果,消息发送到了之制定队列 则返回一个ack通知发送者,
如果队列没有接收到消息,则会返回一个nack,会让你去进行重试
// 将channel 设置称为 Confirm 模式
channel.confirmSelect();
if (channel.waitForCofirms()){
// 消息发送成功
}
2、消息队列丢数据
进行消息的持久化,这个持久化配置可以和confirm机制配合使用,
你可以在消息持久化磁盘后, 再给生产者发送一个Ack信号。
那么如何持久化呢?
这里顺便说一下吧,其实也很容易,就下面两步
1. 将queue的持久化标识durable设置为true,则代表是一个持久的队列
2. 发送消息的时候将deliveryMode=2
路由保证
交换机到队列
mandtory = true + ReturnListener
指定交换机的备份交换机
消息持久化
// 1、队列持久化
@Bean
public Queue logUserQueue() {
return new Queue("log.user.queue.name", true);
}
// 2、交换机持久化
@Bean public DirectExchange logUserExchange() {
return new DirectExchange("log.user.exchange", true, false);
}
// 3、消息持久化
public static Message objToMsg(Object obj) {
if (null == obj) {
return null;
}
Message message = MessageBuilder.withBody(JsonUtil.objToStr(obj).getBytes()).build();
message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
// 消息持久化
message.getMessageProperties().setContentType(MessageProperties.CONTENT_TYPE_JSON);
return message;
}
// 4、集群
注意:
1、只持久化消息,不持久化队列,服务器重启,消息还是会消失
2、如果消息中间件存放的消息满的情况下,达到消息队列的容量阈值,中间件会拒绝接收生产者的消息
3、消息确认机制,确认成功投递了消息到消息中间件
4、消费者如果消费消息失败的情况下,手动实现ack (死信队列)
核心:通过消息确认机制,消息投递成功或是消费成功之后,消息才会被删除
3、消费者丢数据
这种一般发生在消费者自动确认的情况下
消费者虽然受到消息之后,自动发了ack,此时服务器宕机了,此时消息就丢失了
解决办法:关闭自动ack,采用手动ack的方式