高级程序员知识学习(RabbitMQ相关知识)

RabbitMQ高级级消息队列协议。是一种的跨进程的通信的机制,用于上下游之间的管道方式的传递的消息。RabbitMQ的作用是解耦、异步、削峰。

为什么使用MQ?MQ的优点:

  1. 异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。
  2. 应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。
  3. 流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
  4. 日志处理 - 解决大量日志传输。
  5. 消息通讯 - 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等

MQ的缺点:

系统可用性降低:本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低;

系统复杂度提高:加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。

一致性问题:A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C系统写库失败了,咋整?你这数据就不一致了。

RabbitMQ的原理

AMQP协议: 高级消息队列协议

应用程序和Rabbit Server之间会创建一个TCP连接,一旦TCP打开,并通过了认证,认证就是你试图连接Rabbit之前发送的Rabbit服务器连接信息和用户名和密码,有点像程序连接数据库,认证通过你的应用程序和Rabbit就创建了一条AMQP信道(Channel)。信道是创建在“真实”TCP上的虚拟连接,AMQP命令都是通过信道发送出去的,每个信道都会有一个唯一的ID,不论是发布消息,订阅队列或者介绍消息都是通过信道完成的。

包括:ConnectionFactory(连接管理器)、Channel(信道)、Exchange(交换器)、Queue(队列)、RoutingKey(路由键)、BindingKey(绑定键)。

ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用;

Channel(信道):消息推送使用的通道;

Exchange(交换器):用于接受、分配消息;

Queue(队列):用于存储生产者的消息;

RoutingKey(路由键):用于把生成者的数据分配到交换器上;

BindingKey(绑定键):用于把交换器的消息绑定到队列上;

RabbitMQ-理解消息通信-虚拟主机和隔离

Virtual Host的资源的隔离:

每个RabbitMQ服务器都能创建虚拟的消息服务器,我们称之为虚拟主机(vhost)每一个vhost本质上是一个mini版的RabbitMQ服务器,拥有自己的队列、交换器和绑定等等。更重要的是,他拥有自己的权限机制,这使得你能够安全地使用一个RabbitMQ服务器来服务众多的应用程序。vhost就像是虚拟机之与物理服务器一样:他们在各个实例间提供逻辑上的分离,允许你为不同程序安全保密地运行数据,它既能将同一个Rabbit的众多客户区分开来,又可以避免队列和交换器命名冲突。

vhost是AMQP概念的基础,你必须在连接时进行指定。RabbitMQ包含了一个开箱即用的默认vhost“/”,如果你不需要多个vhost,那么就使用默认的吧,使用缺省的guest用户名和密码guest就可以访问默认的vhost。

当你在RabbitMQ集群上创建vhost,整个集群上都会创建该vhost,vhost不仅消除了为基础架构中的每一层运行一个RabbitMQ服务器的需要,同样也避免了为每一层创建不同集群VHost:vhost去做第一层的区分,虚拟主机,工作组等,它默认是/

Channel:创建了客户端到Broker之间的连接后,客户端还是不能发送消息的。需要为每一个Connection创建Channel,AMQP协议规定只有通过Channel才能执行AMQP的命令。一个Connection可以包含多个Channel。之所以需要Channel,是因为TCP连接的建立和释放都是十分昂贵的,如果一个客户端每一个线程都需要与Broker交互,如果每一个线程都建立一个TCP连接,暂且不考虑TCP连接是否浪费,操作系统也无法承受每秒建立如此多的TCP连接。

RabbitMQ的消息路由机制

Exchange的路由协议:

1 Direct exchange

2 Topic exchange(可以使用通配符)

3 Fanout exchange:

RabbitMQ中的消息可靠性机制

MQ中可能存在消息消息的丢失。其中消息的丢失又分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息。消息不可靠的情况可能是消息丢失,劫持等原因;

生产者丢失消息:(解决方案:RabbitMQ提供transaction和confirm模式

从生产者弄丢数据这个角度来看,RabbitMQ提供transaction和confirm模式来确保生产者不丢消息;transaction机制就是说:发送消息前,开启事务channel.txSelect(),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务channel.txCommit()。然而,这种方式有个缺点:吞吐量下降;confirm模式用的居多:一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后;rabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了;如果rabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

消息队列丢数据:(解决方案:消息持久化。处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。

这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。

那么如何持久化呢?1将queue的持久化标识durable设置为true,则代表是一个持久的队列2发送消息的时候将deliveryMode=2。

这样设置以后,即使rabbitMQ挂了,重启后也能恢复数据

消费者丢失消息:(解决方案:消费者丢数据一般是因为采用了自动确认消息模式,改为手动确认消息即可!

消费者在收到消息之后,处理消息之前,会自动回复RabbitMQ已收到消息;如果这时处理消息失败,就会丢失该消息;解决方案:处理消息成功后,手动回复确认消息。

RabbitMQ中的如何保证消息的可靠性的解决方案:

1保证消息不丢失(三步)

1开启事务(不推荐)
2开启confirm(推荐)
3开启RabbitMQ持久化(交换机、队列、消息)
4关闭RabbitMQ自动ack(改成手动)

2、保证消息不重复消费
1幂等性(每个消息用一个唯一标识来区分,消费前先判断标识有没有被消费过,若已消费过,则直接ACK)

3、RabbitMQ如何保证消息的顺序性
将消息放入同一个交换机,交给同一个队列,这个队列只有一个消费者,消费者只允许同时开启一个线程

4RabbitMQ消息重试机制
消费者在消费消息的时候,如果消费者业务逻辑出现程序异常,这时候应该如何处理?
答案:使用消息重试机制(SpringBoot默认3次消息重试机制)

如何合适选择重试机制:1消费者取到消息后,调用第三方接口,接口无法访问,需要使用重试机制2消费者取到消息后,抛出数据转换异常,不需要重试机制,需要发布者进行解决。

5、SpringBoot消息重试机制
@EnableRetry注解:表示启用重试机制(value表示哪些异常需要触发重试,maxAttempts设置最大重试次数,delay表示重试的延迟时间,multiplier表示上一次延时时间是这一次的倍数)@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000, multiplier = 1.5))

@Recover注解:当重试次数达到设置的最大次数的时候,程序还是执行异常,调用的回调函数。

6、RabbitMQ死信队列:死信队列是当消息在一个队列因为下列原因:

a、消息被拒绝(basic.reject或basic.nack)并且requeue=false.

b、消息TTL过期

c、队列达到最大长度(队列满了,数据无法添加到mq中)

变成了“死信队列”后被重新投递(publish)到另一个Exchange,然后重新消费。说白了就是没有被消费的消息换个地方重新被消费

7、RabbitMQ解决分布式事务

经典案例,以目前流行的外卖为例,用户下单后,调用订单服务,订单服务调用派单系统通知送外卖人员送单,这时候订单系统与派单系统采用MQ异步通讯。

RabbitMQ解决分布式事务原理。答案:采用最终一致性原理,就是采用弱一致性原理。

需要保证以下三要素:

a、确保生产者一定要将数据投递到MQ服务器中(采用MQ消息确认机制)

b、确保消费者能够正确消费消息,采用手动ACK模式(注意重试、幂等性问题)

c、如何保证第一个事务先执行,采用补偿机制,在创建一个补单消费者进行监听,如果订单没有创建成功,进行补单。(如果第一个事务中出错,补单消费者会在重新执行一次第一个事务,例如第一个事务是添加订单表,如果失败在补单的时候重新生成订单记录,由于订单号唯一,所以不会重复)

RabbitMQ的消息补偿机制

除了以上的保障措施之外,为了防止生产者发送消息失败或者接收 RabbitMQ confirm 的时候网络断掉等,我们还需要一套完善的消息补偿机制,接下来我们会介绍目前业界主流的两种方案。

消息落库,对消息进行状态标记

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pYm0yc2I1M2xSaHo0NTV0eTJtWmdYOWFjQXNYeEd1NkYycm53VEFoUUk3ZzNrRkpib291MXd4TUJvaWJBZGhJd0lBTllCY3BPNGVRRDNXalNMVWJZenpnLzY0MA?x-oss-process=image/format,png

Step 1:首先生产端处理完业务数据,然后在发送消息前先持久化到消息记录表

Step 2:发送消息给 RabbitMQ,采用 confirm 机制

Step 3:监听 RabbitMQ 的 confirm 回调

Step 4:根据 message id 以及回调的信息更新消息状态

Step 5:分布式定时任务获取未发送成功的消息,然后判断重试次数是否大于 N 次,N 为业务系统定义的最大重试次数

Step 6:将重试次数 < N 次的消息重新发送给 RabbitMQ

Step 7:将重试次数 > N 次的消息降级为人工处理

这种方案能保证即使消息发送失败了,或者其中某一环突然掉链子了,但只要消息成功入库了,就能通过定时任务重试机制发送给 RabbitMQ,然后消费端再通过 ack 机制去消费消息,需要注意的是,消费端的消费逻辑必须幂等。

延迟投递,做二次确认,回调检查

https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pYm0yc2I1M2xSaHo0NTV0eTJtWmdYOWFjQXNYeEd1NkZHd1lFY1VrUGFJNGpuaWEzYmpOcHdPbTNvdWljVEt1TU9SNVlCYmRtN2FtenMzNmliUjNsd3FxdmcvNjQw?x-oss-process=image/format,png

Step 1:发送消息给 RabbitMQ

Step 2:发送一条一样的延迟消息给 Callback Server

Step 3:消费者消费消息

Step 4:如果消息消费成功了,则将消息发送给 Callback Server,注意这里不是采用 ack 机制

Step 5:Callback Server 监听到消费者发来的消息,知道有条消息消费成功了,然后将消息记录到 DB

Step 6:Callback Server 监听生产端发来的延迟消息

Step 7:Callback Server 到数据库查询消息是否存在

Step 8:如果消息存在,则不做处理,如果消息不存在,则发起一个 RPC 请求到生产端,告诉它消息发送失败了,需要重新发送一遍,然后生产端重新发送即时和延迟两条消息出去,按照流程再走一遍。

上面那种方案的弊端是显而易见的,在整个消息发送以及确认流程中,每条消息都需要入库再发送,然后收到 confirm 去更新消息状态,如果在高并发场景下,数据库毫无疑问会成为一个瓶颈。而相较于上面的方案,延迟投递二次确认的方案对数据库的操作更少,并且对正常消息的发送和消费过程不会有太多干预,但是如果在没有消费者或者消费者全部断开连接的情况下会造成大量重复消息积压,所以一般在高并发场景下配合一些的策略使用。

RabbitMQ的消息持久化原理

1.交换器的持久化

交换器的持久化是在声明交换器的时候,将durable设置为true。如果交换器不设置持久化,那么在RabbitMQ交换器服务重启之后,相关的交换器信息会丢失,不过消息不会丢失,但是不能将消息发送到这个交换器。

2.队列对持久化

队列的持久化在声明队列的时候,将durable设置为true。如果队列不设置持久化,那么RabbitMQ交换器服务重启之后,相关的队列信息会丢失,同时队列中的消息也会丢失。

3.消息的持久化

消息的持久化是在BasicProperties中设置deliveryMode设置为2。队列的持久化能保证本身的元数据不会因为异常而丢失,但是不能保证内部所存在的消息不会丢失。要确保消息不丢失,需要将消息持久化。

如果将所有的消息都进行持久化操作,这样会严重影响RabbitMQ的性能。写入磁盘的速度比写入内存的速度慢很多。所以要在可靠性和吞吐量之间做权衡。

将交换器、队列和消息都进行持久化操作后,也并不能保证消息一定不会丢失。

1.对于消费者来说,如果在订阅消息的时候,将autoAck设置为true,那么消费者接收到消息后,还没有处理,就出现了异常挂掉了,此时,队列中已经将消息删除,消费者不能够在收到消息。这种情况可以将autoAck设置为false,进行手动确认。

2.在持久化后的消息存入Rabbit'MQ之后,还需要一段时间才能存入磁盘。RabbitMQ并不会为每条消息都进行同步存盘,可能仅仅是保存到操作系统缓存之中而不是物理磁盘。如果在这段时间,服务器宕机或者重启,消息还没来得及保存到磁盘当中,从而丢失。对于这种情况,可以引入RabiitMQ镜像队列机制。

RabbitMQ的高可用集群原理

采用三个节点组成了一个RabbitMQ的集群,Exchange A的元数据信息在所有节点上是一致的,而Queue(存放消息的队列)的完整数据则只会存在于它所创建的那个节点上。,其他节点只知道这个queue的metadata信息和一个指向queue的owner node的指针。

RabbitMQ集群元数据的同步:

RabbitMQ集群会始终同步四种类型的内部元数据(类似索引

队列元数据:队列名称和它的属性,

交换器元数据:交换器名称、类型和属,

绑定元数据:一张简单的表格展示了如何将消息路由到队列,

vhost元数据:为vhost内的队列、交换器和绑定提供命名空间和安全属性;

因此,当用户访问其中任何一个RabbitMQ节点时,通过rabbitmqctl查询到的queue/user/exchange/vhost等信息都是相同的。

为何RabbitMQ集群仅采用元数据同步的方式

1存储空间,如果每个集群节点都拥有所有Queue的完全数据拷贝,那么每个节点的存储空间会非常大,集群的消息积压能力会非常弱(无法通过集群节点的扩容提高消息积压能力);2性能,消息的发布者需要将消息复制到每一个集群节点,对于持久化消息,网络和磁盘同步复制的开销都会明显增加。

RabbitMQ的集群的工作原理图

场景1:客户端直接连接队列所在节点

如果有一个消息生产者或者消息消费者通过amqp-client的客户端连接至节点1进行消息的发布或者订阅,那么此时的集群中的消息收发只与节点1相关,这个没有任何问题;如果客户端相连的是节点2或者节点3(队列1数据不在该节点上),那么情况又会是怎么样呢?

场景2:客户端连接的是非队列数据所在节点

如果消息生产者所连接的是节点2或者节点3,此时队列1的完整数据不在该两个节点上,那么在发送消息过程中这两个节点主要起了一个路由转发作用,根据这两个节点上的元数据(也就是上文提到的:指向queue的owner node的指针)转发至节点1上,最终发送的消息还是会存储至节点1的队列1上。

镜像队列?

RabbitMQ的事务管理

正常情况下,如果消息经过交换器进入队列就可以完成消息的持久化,但如果消息在没有到达broker之前出现意外,那就造成消息丢失,有没有办法可以解决这个问题?

RabbitMQ有两种方式来解决这个问题:1通过AMQP提供的事务机制实现;2使用发送者确认模式实现。

事务使用:事务的实现主要是对信道(Channel)的设置,主要的方法有三个:

    channel.txSelect()声明启动事务模式;

    channel.txComment()提交事务;

    channel.txRollback()回滚事务;

假设消费者模式中使用了事务,并且在消息确认之后进行了事务回滚,那么RabbitMQ会产生什么样的变化?结果分为两种情况:

1、autoAck=false手动应对的时候是支持事务的,也就是说即使你已经手动确认了消息已经收到了,但在确认消息会等事务的返回解决之后,在做决定是确认消息还是重新放回队列,如果你手动确认现在之后,又回滚了事务,那么已事务回滚为主,此条消息会重新放回队列;

2、autoAck=true如果自定确认为true的情况是不支持事务的,也就是说你即使在收到消息之后在回滚事务也是于事无补的,队列已经把消息移除了;

二、Confirm发送方确认模式

方式一:channel.waitForConfirms()普通发送方确认模式;

方式二:channel.waitForConfirmsOrDie()批量确认模式;

方式三:channel.addConfirmListener()异步监听发送方确认模式;

RabbitMQ的常见问题和解决方案

MQ 的常见问题有:消息的顺序问题、消息的重复问题

消息的顺序问题:消息有序指的是可以按照消息的发送顺序来消费。

假如生产者产生了 2 条消息:M1、M2,假定 M1 发送到 S1,M2 发送到 S2,如果要保证 M1 先于 M2 被消费,怎么做?

https://imgconvert.csdnimg.cn/aHR0cHM6Ly91c2VyLWdvbGQtY2RuLnhpdHUuaW8vMjAxOS81LzQvMTZhODMwNzVlMjc1NTFiMA?x-oss-process=image/format,png

解决方案:保证生产者 - MQServer - 消费者是一对一对一的关系

缺陷:

    并行度就会成为消息系统的瓶颈(吞吐量不够)

    更多的异常处理,比如:只要消费端出现问题,就会导致整个处理流程阻塞,我们不得不花费更多的精力来解决阻塞的问题。 (2)通过合理的设计或者将问题分解来规避。

    不关注乱序的应用实际大量存在

    队列无序并不意味着消息无序 所以从业务层面来保证消息的顺序而不仅仅是依赖于消息系统,是一种更合理的方式

消息的重复问题 造成消息重复的根本原因是:网络不可达

所以解决这个问题的办法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?

消费端处理消息的业务逻辑保持幂等性。只要保持幂等性,不管来多少条重复消息,最后处理的结果都一样。保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现。利用一张日志表来记录已经处理成功的消息的 ID,如果新到的消息 ID 已经在日志表中,那么就不再处理这条消息。

MQ常问道的面试问题

为什么不通过TCP直接发送命令?

对于操作系统来说创建和销毁TCP会话是非常昂贵的开销,假设高峰期每秒有成千上万条连接,每个连接都要创建一条TCP会话,这就造成了TCP连接的巨大浪费,而且操作系统每秒能创建的TCP也是有限的,因此很快就会遇到系统瓶颈。如果我们每个请求都使用一条TCP连接,既满足了性能的需要,又能确保每个连接的私密性,这就是引入信道概念的原因

RabbitMQ的消息确认机制:也是RabbitMQ中的事务机制。

在RocketMQ里事务消息处理分为三个个阶段

第一阶段是把消息传递给MQ,但是消息对消费端不可见,实际上数据已经发送到了broker上,会拿到消息的地址。(发送prepared消息)

第二阶段是执行本地事务。

第三个阶段是通过第一阶段拿到的地址去访问消息,并修改消息的状态。如果本地消息处理成功返回commit,则修改在broker上的消息的状态为对消费端可见,如果失败返回rollback,则对消费端不可见。(发送确认消息给broker)

为了防止确认消息发送失败,RocketMQ会定期扫描集群中的事务消息,如果发现有prepared消息(状态没有改变),它会向生产者确认,该消息到底是否继续发送给消费端,这样就保证了本地事务和消息发送的同时成功或者同时失败。

https://img-blog.csdn.net/20180730153556356?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NtdWdhb3lp/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70

如果你要确保说写RabbitMQ的消息别丢,可以开启confirm模式,在生产者那里设置开启confirm模式之后,你每次写的消息都会分配一个唯一的id,然后如果写入了RabbitMQ中,RabbitMQ会给你回传一个ack消息,告诉你说这个消息ok了。如果RabbitMQ没能处理这个消息,会回调你一个nack接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息id的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。

怎么保证消息不重复消费?

给每一个消息添加一个全局唯一的id。消费者接收到消息时,将消息对象进行MD5加密,作为消息唯一性。如果发现messageObj(发送到mq的数据)已经存在,则忽略,进而保证消息不被重复消费。

怎解决消息的挤压的问题?为什么产生消息挤压,怎么处理?

解决方案:

这种时候只能操作临时扩容,以更快的速度去消费数据了。具体操作步骤和思路如下:

①先修复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉。

②临时建立好原先10倍或者20倍的queue数量(新建一个topic,partition是原来的10倍)。

③然后写一个临时分发消息的consumer程序,这个程序部署上去消费积压的消息,消费之后不做耗时处理,直接均匀轮询写入临时建好分10数量的queue里面。

④紧接着征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的消息。

⑤这种做法相当于临时将queue资源和consumer资源扩大10倍,以正常速度的10倍来消费消息。

⑥等快速消费完了之后,恢复原来的部署架构,重新用原来的consumer机器来消费消息。

MQ中消息丢失的三种情况——生产者、消息队列、消费者

https://img2020.cnblogs.com/blog/1384439/202003/1384439-20200324152227931-2109909666.png

生产者传输过程中丢失数据——Confirm机制

所以一般来说,如果你要确保说写 RabbitMQ 的消息别丢,可以开启confirm模式,在生产者那里设置开启confirm模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个ack消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你一个nack接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。

消息队列自身丢失——持久化处理

就是 RabbitMQ 自己弄丢了数据,这个你必须开启 RabbitMQ 的持久化,就是消息写入之后会持久化到磁盘,哪怕是 RabbitMQ 自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢。除非极其罕见的是,RabbitMQ 还没持久化,自己就挂了,可能导致少量数据丢失,但是这个概率较小。

消费者还未处理就宕机了

这个时候得用 RabbitMQ 提供的ack机制,简单来说,就是你关闭 RabbitMQ 的自动ack,可以通过一个 api 来调用就行,然后每次你自己代码里确保处理完的时候,再在程序里ack一把。这样的话,如果你还没处理完,不就没有ack?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。

https://img2020.cnblogs.com/blog/1384439/202003/1384439-20200324154920126-495907151.png

大量消息在 mq 里积压了几个小时了还没解决?

一般这个时候,只能临时紧急扩容了,具体操作步骤和思路如下:

先修复 consumer 的问题,确保其恢复消费速度,然后将现有 consumer 都停掉。新建一个 topic,partition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量。

然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 queue。

接着临时征用 10倍的机器来部署consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的 10 倍速度来消费数据。等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先consumer 机器来消费消息。

RabbitMQ中的消息过期失效了(设置过期的时间但是存在消息的大量的丢失)

RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。这就不是说数据会大量积压在 mq 里,而是大量的数据会直接搞丢。这个情况下,就不是说要增加 consumer 消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去。

为什么不应该对所有的 message 都使用持久化机制?

首先,必然导致性能的下降,因为写磁盘比写 RAM 慢的多,message 的吞吐量可能有 10 倍的差距。其次,message 的持久化机制用在 RabbitMQ 的内置 cluster 方案时会出现问题。可能存在问题是:若 message 设置了 persistent 属性,但 queue 未设置 durable 属性,那么当该 queue的owner node出现异常后,在未重建该 queue 前,发往该 queue 的 message 将被 blackholed ;

若 message 设置了 persistent 属性,同时 queue 也设置了 durable 属性,那么当 queue 的 owner node 异常且无法重启的情况下,则该 queue 无法在其他 node 上重建,只能等待其 owner node重启后,才能恢复queue的使用,而在这段时间内发送给该 queue 的 message 将被 blackholed 。

所以,是否要对 message 进行持久化,需要综合考虑性能需要,以及可能遇到的问题。

消息堆积的问题:

  消息丢失:

 

 

 有序消费的问题

猜你喜欢

转载自blog.csdn.net/weixin_41605937/article/details/106002962