rabbitmq 消息可靠性投递

751 阅读2分钟

解决mq消息可靠性投递,从三个方面解决这个问题

1、消息生产者

2、消息在队列中

3、消息消费者


如图:

image.png


1、发送者丢数据

事务模式

// 将channel设置成事务模式 
channel.tsSelect(); 

// 提交事务 
channel.txCommit(); 

// 事务回滚 
channel.txRooback(); 

// transaction 模式,开启事务, 然后发送消息,如果中途发送出现异常, 
// 事务回滚,如果没有异常,事务提交 
//  这种方式会导致吞吐量下降。 所以线上用下一种方式居多

image.png

image.png


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的方式