RabbitMQ实现可靠性传输 理论篇

本文为理论版,另有代码版:点击跳转


本文内容是在参考了大量资料的同时,自己实操得出来的结论。
笔者代码环境:

springboot 2.1.9.RELEASE + amqp-client-5.4.3.jar
在这里插入图片描述

如图所述,需要从三个方面解决问题:

生产端,MQBroker,消费端。

生产者确认模式

定义:

  • 当消息确认到达Broker后回调,(即只确认是否正确到达 Exchange 中),broker会发送一个确认ack给生产者(若设置了持久化,则在持久化完成后再发出ack
  • 如果RabbitMQ因为自身内部错误导致消息丢失,就会发送nack

纠错:

  1. 正确到达exchange 即可,而不是一些资料"投递到所有匹配的队列"

  2. MQ的确会为所有消息分配"Id"。但那是投递之后的事。

    而且据笔者测试,deliveryTag,跟是否开启生产者确认模式没关系。

生产者确认模式有三种:

  1. 普通confirm:串行,有一条消息发一条,等待confirm再发
  2. 批量confirm:定量发送,等待全部confrim。但失败时批量重发
  3. 异步confrim: 常用,直接发送,无需等待confirm,异步进行。

而一般采用异步confirm,所以发送消息之前,需要把消息存起来。所以我们需要为消息分配全局唯一的Id,与消息内容一一对应。而放内存有内存溢出风险。所以一般用redis等作记录

生产者也需要监听Broker发送的通知,根据ack / nack 进行确认。

而具体实现时,需要注意:

  • 限制重发次数:超限应标志为任务异常,通过人工处理,避免一直重发浪费资源
  • 定时扫描未确认消息,因为broker的响应可能会丢失

如何保证投递成功

对应:生产者丢数据

两种方案,互斥,二选一

  1. 利用RabbitMQ提供的事务,失败重发(同步,性能很差
  2. 生产者确认confirm 模式(异步模式

但注意,这里处理的是消息,是能被交换器路由到队列上的。

若消息无法被路由,如交换器没有匹配的队列,则需要用另一种办法。↓


无法被路由的消息处理
  1. 直接丢弃,默认策略

  2. 失败确认
    发布消息时,将mandatory设置为true,且实现return相关的监听器(returnCallback)。

    这样在消息无法被路由时,生产者会收到相关通知消息。就可以自主选择如何处理消息了。

    拓:这种情况也会触发生产者确认的confirm接口,因为如定义:只要投递到了exchange,就回调确认。

    本情况也属于成功到了exchange的。

  3. 声明交换机时,指定备份的交换机

arguments.put("alternate-exchange","备份交换机名");

exchangeDeclare.(xxx,arguments);

拓:若方案2、3一起使用,方案3生效,方案2失效


如何持久化消息队列

对应RabbitMQ丢数据

设置**持久化**,两个步骤:必须都设置

  1. 创建 exchange、queue 时,设置为持久化 durable = true

    (其实默认是持久化)

    会持久化 queue 的元数据,但不会持久化 queue 里的消息

  2. 因此需要单独设置消息的持久化:

    发送消息时,将消息Properties的 deliveryMode=PERSISTENT

无法保障100%不丢失,因为可能持久化完成前就宕机。

(若设置持久化,会在持久化完成后再发出ack

若有集群,可以引入mirrored-queue即镜像队列,但也有整个集群都挂掉的可能。


如何保证消费成功(或限流处理)

对应消费端丢数据,即消费完成前,宕机

RabbitMQ默认是自动ack的。

自动ack 的处理(也是缺点):

  1. 消息失败,则重新入队,但入队还是在队列首部,很容易造成死循环

    (可通过 default-requeue-rejected覆盖)

  2. 只要有消息,就会源源不断地发送给客户端,而不管客户端能否消费的完(即无法限流

注意不要搞混了noneautonone才是发送了就不管,auto只是自动回发ack。

解决:

采用接收方确认机制:ackMode = manual,即 :

消费者接收消息后,手动确认 / 拒绝消息,响应给Broker


但因为rabbitMQ没有超时机制

因此Broker判断是否重发消息的唯一标准是:连接是否中断

  • 连接未断开,但也未收到任何确认/拒绝。

    则认为该消费者繁忙,不再给该消费者分发更多的消息。

    但也不会给其他消费者发送该信息,直到消费者回复,或者连接断开(断开处理如下)

  • 在收到ack / nack 前,连接断开(无论哪一方),认为消息没有被消费,会重新分发给下一个订阅的消费者。

    (也可能是同一个消费者。但不管如何,都会存在重复消费的隐患,需要去重等处理)

拓:

在手动确认的模式下,可以通过指定prefetch,做到限流处理

该属性的定义是:

消费者可能未完成的最大未确认消息数,即缓冲区大小

比如为N,broker在发送了N条消息后,如果消费者没有响应消息,则不再分发消息给该消费者。直到它回复了消息,才继续分发。


本文完,有误欢迎指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值