面试题MQ 消息可靠性 看完还不会你报警吧

154 阅读8分钟

RabbitMQ 消息可靠性

RabbitMQ如何保证消息可靠性?

RabbitMQ如何保证消息不丢失?

RabbitMQ发送与消费消息的模型

图片.png

消息丢失的情况

在RabbitMQ中,消息在发送过程中会发生消息不可靠的情况

什么是消息不可靠? 假如在项目中遇到需要保证消息一定被消费的场景时,而当消息没有被正常消费,出现了消息丢失的异常情况,这就是消息的不可靠性

那么消息丢失有那些情况

一生产者发送消息未到达交换机:

这种情况可能是由于生产者与RabbitMQ之间的网络故障导致的,例如连接中断或RabbitMQ服务器不可用。

二消息到达交换机,没有正确路由到队列:

当消息到达交换机后,如果没有匹配的绑定将消息路由到队列,消息将会被丢弃。这可能是由于交换机配置错误、队列绑定错误、路由键不匹配等引起的问题。

三MQ宕机,队列中的消息不见了:

如果RabbitMQ服务器宕机或崩溃,尚未持久化的消息会丢失。即使消息已经被发送到了队列,如果队列、交换机和消息都没有进行持久化配置,那么在MQ宕机后,这些消息将会丢失。

四消费者收到消息,还没消费完,消费者宕机:

当消费者接收到消息后,如果在消息完全处理之前宕机或崩溃,消息会重新返回到队列中,等待其他消费者将其重新消费。这种情况下可能会发生重复消费的问题,需要根据业务的幂等性要求来处理。消费者可以使用消费者ack机制(Consumer Acknowledgement),在成功处理消息后向RabbitMQ发送确认消息。如果消费者宕机,未发送确认消息,则RabbitMQ会将消息重新分发给其他消费者。

如何保证消息不丢失

一消息持久化:

RabbitMQ支持将消息标记为持久化,这样即使在RabbitMQ服务器发生故障时,消息也不会丢失。可以通过将消息的delivery mode设置为PERSISTENT(默认为2)来实现消息的持久化。

二:超时设置(Time-to-live):

可以为消息设置超时时间,一旦消息在指定时间内没有被消费者接收,则消息会被标记为"dead",进而可由其他消费者进行处理。

三:集群和镜像队列:

将RabbitMQ部署在多个节点上组成集群,可以提高消息的可靠性和可用性。镜像队列可以在集群中的多个节点上备份队列,确保即使某个节点发生故障,消息仍然可以被处理。

四发布确认(Publish Confirmation):

图片.png

发布确认是指生产者发送消息后,等待RabbitMQ确认消息是否已经正确接收和持久化。通过启用发布确认模式,生产者可以确认消息是否成功发送到RabbitMQ服务器,并根据确认结果进行相应的处理。

1.publisher-confirm

消息成功投递到交换机,返回ack

消息未成功投递到交换机,返回nack

2、publisher-return

消息未正确到达队列,返回ack及失败原因

五消费者确认(Consumer Acknowledgement):

图片.png

消费者在消费消息之后,可以向RabbitMQ发送ack ,消息已被消费的信号。RabbitMQ接收到确认后会删除对应的消息,否则会将消息重新分发给其他消费者。

这其中消费者ack确认机制中有三种模式

1.none 关闭ack

只要消息到达消费者,Spring直接返回ack到MQ,MQ收到ack,会把队列中的消息删除

这种情况消息还是会丢失

2.manual 手动ack

消费者成功消费,手动调用API给MQ返回ack,确认消息已被消费

消费者消费失败,手动调用API给MQ返回nack,告诉MQ消息没有被消费,并且让消息重回队列

3.auto 自动ack (默认)

消费者消费消息不出异常,返回ack给MQ。消费消息出异常了,返回nack,把消息重回队列

在消费消息时,可以根据不同的设置不同自动ack策略。以下是一些常见的自动ack策略:

  1. 即时自动ack:消费者在成功处理消息后立即返回ack,表示消息已被消费,并从队列中移除。
  2. 延迟自动ack:消费者在成功处理一定数量的消息后,才进行自动ack。这种策略可以用于批量处理消息,以减少频繁的ack操作。
  3. 定时自动ack:在指定的时间间隔内,自动ack已消费的消息。这种策略可以防止因消息处理延迟而导致的过多淤积在队列中的消息。
  4. 条件策略:基于特定条件来确定自动ack的时机。例如,当消费者处理消息成功且相关业务逻辑满足特定条件时,才进行自动ack。
本地重试:

本地重试策略是指在应用程序或服务中处理失败的操作时,通过在本地进行自动重试来提高操作成功率的一种策略。以下是一些常见的本地重试策略:

  1. 重试次数限制

    设置一个固定的重试次数上限,例如3次。当操作失败时,自动进行多次重试,直到达到重试次数限制或操作成功为止。

消费者在消费消息时,如果失败了,则在本地重试,重新消费,如果达到重试次数,还是失败,则返回ack,不重回队列,MQ会删除队列中的消息

如果最后还是失败,可以采取以下的失败策略:

RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息。默认就是这种方式

ImmediateRequeueMessageRecoverer:重试耗尽后,返回nack,消息重新入队

RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机

图片.png

2.限制重试时间:

设置一个最大的重试时间,例如30秒钟。无论重试次数如何,当重试时间超过设定的限制时,停止重试并返回失败结果。

3.逐级重试(Incremental Retries:

当遇到特定类型的错误时,可以根据错误类型逐级增加重试。例如,针对网络超时错误,可以先进行快速的短时间重试,然后逐渐增加重试间隔和次数。

重试策略的设计需要考虑到操作的性质、系统的容错能力和对错误类型的理解。适当的重试策略可以提高操作的成功率,减少对用户的影响,并改善应用程序或服务的可靠性。

最后说一说本地重试策略和自动ack的区别

本地重试策略用于处理消费消息失败的情况,而自动ack策略用于成功消费消息时,向消息队列发送确认信号。

保证消息正确消费

以上是确保消息不丢失的几种策略。这几种策略中重点就是消息持久化,发布确认,消费者确认,现在我们继续说说如何何确保消息被正确消费的策略。

第一种策略是消费者批量处理消息。在消费者成功处理一定数量的消息后,才进行自动确认。这种批量处理的方式可以减少频繁的确认操作,提高处理效率。

第二种策略是消费者幂等性设计。幂等性是指无论对同一消息操作多少次,最终的结果都是一致的。通过设计消费者的处理逻辑为幂等操作,即使消费者意外重复处理消息,也不会影响最终结果。

除了幂等性,消费者还可以使用事务来确保消息的正确消费。在使用事务的情况下,消费者将消息处理操作放在一个事务中,并在处理完成后提交事务。如果发生异常或错误,消费者可以回滚事务,这样消息将不会被确认,从而避免了消息的丢失和处理不一致的情况。

另一种策略是消费者的异常处理机制。当消费者在处理消息时发生异常,可以通过捕获异常并进行适当的处理,例如记录日志、发送警报或进行补偿操作。这样可以保证消费者的健壮性,并尽可能地避免消息的丢失。

还有监控和报警机制。通过实时监控消息队列的状态和消费者的健康情况,可以及时发现异常并采取相应的措施。配合报警机制,可以提供及时的提醒和响应。

总结一下,要确保消息不丢失且被正确消费,我们可以采取消息持久化、超时设置、集群和镜像队列、发布确认、消费者确认、消息重试、消费者批量处理、幂等性设计、事务处理、异常处理、监控和报警机制策略。