RabbitMQ消息可靠性保障

发布时间 2023-10-11 11:37:57作者: 小张同学哈

消息丢失的情况

publisher 在往rabbitmq发送消息时,可能由于网络问题导致消息丢失

rabbitmq在投递消息时,找不到匹配的队列消息时,可能导致消息丢失,rabbitmq队列存储消息在未push给消费者之前,如果服务器故障可能导致消息丢失

rabbitmq在将消息push给消费者时,可能由于网络问题导致消息丢失

也就是说消息可能在发送过程中、rabbitmq服务器上、消息接收过程中丢失。

下面分类阐述上述三种情况,如何解决。

发送过程

  publisher-confirm

  在发送消息时,采用发送方确认(publisher-confirm)机制,通过channel.confirmSelect方法将信道设置为confirm模式。在这种模式下,RabbitMQ收到生产者发送的消息后,会发送一个ack或者nack给生产者。

    • ack: 消息已经正确投递给队列或者其他交换机(如果是持久化消息,那么ack会在消息持久化到磁盘后才会给出)
    • nack: RabbitMQ自身内部错误,导致消息丢失

  publisher-return

  在发送消息时,将mandatory参数设置为true,此时,如果rabbitmq无法根据交换机和路由键找到符合条件的队列进行投递,RabbitMQ会将消息退回给生产者。如果mandatory为false,则消息会被直接丢弃。如果使用了该参数,需要给channel设置ReturnListener来监听RabbitMQ退回的消息。

  持久化消息

  在发送消息时,将消息的durable属性设置为true,告诉RabbitMQ需要将消息持久化。

RabbitMQ服务器

  这里需要保证RabbitMQ的高可用,并且需要防止消息在RabbitMQ集群中丢失。

  RabbitMQ集群中,集群所有节点都保存有vhost、exchange、bindings、queue的元数据信息。但是队列中的message只有在声明这个queue的节点上(队列的所有者节点)有。一旦该节点崩溃,该节点上的队列和队列的绑定关系都丢失了(不过其他节点上也会有这些信息),连接到该节点上的消费者也都下线。,所有投递到该节点的队列的新消息(未来得及持久化的message都会丢失)。此时,想要该节点中的队列返回集群正常工作的唯一方法时恢复该故障节点。

     RabbitMQ集群中,集群所有节点分为内存节点和磁盘节点两类,磁盘节点会将上述元数据信息保存到磁盘,而内存节点只会保存到内存中。RabbitMQ集群应当至少有一个磁盘节点。但如果集群中只有一个磁盘节点,一旦该节点崩溃,虽然RabbitMQ能够正常收发消息,但是无法进行元数据的更改,如创建交换机、队列等操作将无法进行。因此,一般建议所有节点都设置为磁盘节点。

因此在RabbitMQ集群中,需要使用镜像队列或者仲裁队列来保证消息不丢失。

  镜像队列

  在声明队列时,添加额外参数,将队列设置为镜像队列。如:

queue_args = {"x-ha-policy", "all"}
channel.queue_declare(queue="hello",arguments=queue_args)

  这样,发送到RabbitMQ服务器的消息,会被存储到集群中的所有节点上。如果你不想存储到所有节点,而只想消息有一个或者几个备份怎么办?很麻烦,需要将集群节点硬编码到你的代码中,不建议这样做。

  仲裁队列

  在新的RabbitMQ版本中,可以使用冲裁队列。冲裁采用Raft算法,将消息发送到集群中的大多数节点。

     注意事项:

  1. 需要保障消息可靠性时,不要使用备份交换机(AE交换机),AE交换机会导致publisher-return机制失效。因为所有无法投递的消息都会到AE交换机。即使AE交换机不存在,RabbitMQ将消息丢弃也不会退回给生产者

接收过程

  接收过程较为简单,使用ack机制即可。