淘先锋技术网

首页 1 2 3 4 5 6 7

1 RabbitMQ的使用场景?

  1. 解耦。微服务中不同服务之间的消息通信

解耦系统,对于新增的功能可以单独写模块扩展,比如用户确认评价之后,新增了给用户返积分的功能,这个时候不用在业务代码里添加新增积分的功能,只需要把新增积分的接口订阅确认评价的消息队列即可,后面再添加任何功能只需要订阅对应的消息队列即可。

  1. 异步。将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度。
  2. 削峰,并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常
  3. 广播:基于Pub/Sub实现一对多通信
  4. 延迟信息处理,比如10分钟之后给下单未付款的用户发送邮件提醒。

2 如何保证RabbitMQ不被重复消费?

答:正常情况下,消费者在消费消息时,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将消息从消息队列中删除;
但是因为网络传输等故障,确认消息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他消费者。
针对以上问题,一个解决思路:保证消息的唯一性,就算多次传输,不要让消息的多次消费带来影响;保证消息的幂等性。
比如:在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过。

3 如何保证RabbitMQ消息的可靠传输?

  1. 发送方确认模式:将信道设置为confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID.一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一的ID).如果rabbitmq发生内部错误从而导致消息丢失,会发送一条nack(not acknowledged,未确认)消息。发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息 到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。
  2. 接收方确认机制接收方消息确认机制:消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。保证数据的最终一致性;下面罗列几种特殊情况:如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。

4 如何保证RabbitMQ的高可用?

采用镜像集群

5 rabbitmq如何保证消息的顺序性?

  1. 【这条是主要的】生产的消息,必须保证在投递到同一个队列,且消费者只能有一个。防止多个消费者乱序。
  2. 并且发送的时候,不要多线程等并发发送,要同步方式发送。防止多线程乱序发送
  3. 发送完后要采用confirm确认机制。确认发送成功了,才进行下一条发送。否则程序就执行中断进程,为了防止当前消息发送失败,然后下一条又开始发送并且发送成功。

6 rabbitmq如何提高消息的消费速率?

  1. 增加消费者机器
  2. 优化代码,带宽等其他设施

7 rabbitmq有哪几种路由方式?【知识概念】

  1. direct(默认方式):最基础最简单的模式,发送方把消息发送给订阅方,如果有多个订阅者,默认采取轮询的方式进行消息发送。
  2. headers:与 direct 类似,只是性能很差,此类型几乎用不到
  3. fanout:分发模式,把消息分发给所有订阅者
  4. topic:匹配订阅模式,使用正则匹配到消息队列,能匹配到的都能接收到

8 rabbitmq交换机与队列、队列与消费者的绑定关系是什么样的?多个消费者监听一个队列时,消息会重复消费吗?

  1. 交换机与队列,队列与消费者的绑定关系都是多对多
  2. 当一个队列有多个消费者时,会采用轮询方式平均分发,不会重复消费

9 rabbitmq的vhost 是什么?起什么作用?

vhost可以理解为虚拟broker,即 mini-rabbitMQ server.其内部均含有独立的queue、exchange和binding等,但最重要的是,其拥有独立的权限系统,可以做到vhost范围的用户控制。当然,从rabbitMQ的全局角度,vhost可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的vhost中)。

10 rabbitmq消息基于什么传输?

RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制
channel设计的目的:TCP复用
首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和RabbitMQ 就创建了一条 AMQP信道(channel),信道是创建在“真实” tcp 上的虚拟连接,AMQP 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。

11 rabbitmq死信队列和延迟队列的使用?

消息变成死信有以下几种情况:

  • 消息被拒绝(basic.reject / basic.nack),并且requeue = false过期了队列达到最大的长度
  • 消息TTL过期
  • 队列达到最大长度

过期消息:
在 rabbitmq 中存在2种方可设置消息的过期时间,第一种通过对队列进行设置,这种设置后,该队列中所有的消息都存在相同的过期时间,第二种通过对消息本身进行设置,那么每条消息的过期时间都不一样。如果同时使用这2种方法,那么以过期时间小的那个数值为准。当消息达到过期时间还没有被消费,那么这个消息就成为了一个死信消息。

队列设置:在队列申明的时候使用 x-message-ttl 参数,单位为毫秒;单个消息设置:是设置消息属性的 expiration 参数的值,单位为毫秒

延时队列:在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间和死信队列来模拟出延时队列。消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。

12 使用了消息队列会有什么缺点?

1.增加了额外的消息队列技术栈,需要维护,该技术栈如果挂了,也会带来额外的成本
2.系统复杂性增加:要多考虑很多方面的问题,比如一致性问题、如何保证消息不被重复消费,如何保证保证消息可靠传输。因此,需要考虑的东西更多,系统复杂性增大。

13 rabbitmq多个消费者监听一个队列时,消息如何分发?

  1. 轮询:默认策略,消费者轮流,平均的接收消息
  2. 公平分发:根据消费者的能力来分发消息,给空闲的消费者发送更多的消息。当消费者有x 条消息没有响应ACK时,不再给这个消费者发送消息

14 消息在什么时候会变成死信?

  1. 消息拒绝并且没有设置重新入队
  2. 消息过期
  3. 消息堆积,并且队列达到最大长度,先入队的消息会变成DL

15 RabbitMQ如何实现延时队列?

利用TTL(队列的消息存活时间或者消息存活时间),加上死信交换机

16 RabbitMQ如何解决消息幂等性

17 RabbitMQ的集群模式和集群节点类型?

普通模式:默认模式,以两个节点(rabbit01,rabbit02)为例来进行说明,对于Queue来说,消息实体只存在于其中一个节点rabbit01(或者rabbit02),rabbit01和rabbit02两个节点仅有相同的元数据,即队列结构。当消息进入rabbit01节点的Queue后,consumer从rabbit02节点消费时,RabbitMQ会临时在rabbit01,rabbit02间进行消息传输,把A中的消息实体取出并经过B发送给consumer,所以consumer应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理Queue。否则无论consumer连rabbit01或rabbit02,出口总在rabbit01,会产生瓶颈。当rabbit01节点故障后,rabbit02节点无法取到rabbit01节点中还未消费的消息实体。

镜像模式:把需要的队列做成镜像队列,存在与多个节点属于RabibitMQ的HA方案,该模式解决了普通模式中的问题,其实质和普通模式不同之处在于,消息体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取,该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉,所以在对可靠性要求比较高的场合中适用节点分为内存节点(保存状态到内存,但持久化的队列和消息还是会保存到磁盘),磁盘节点(保存状态到内存和磁盘),一个集群中至少需要一个磁盘节点

18 如何自动删除长时间没有消费的消息

  1. 设置队列为TTL
  2. 设置每条消息的过期时间

19 RabbitMQ集群怎么避免重启后消息丢失?

把消息持久化磁盘,保证服务器重启消息不丢失。每个集群中至少有一个物理磁盘,保证消息落入磁盘。

20 RabbitMQ 对集群节点停止顺序有要求吗?

RabbitMQ对集群的停止顺序是有要求的,应该先关闭内存节点,最后关闭磁盘节点。如果顺序相反的话,可能会造成消息的丢失。

21 RabbitMQ 有哪些重要的组件?

RabbitMQ的重要角色有:生产者、消费者、代理
生产者:消息的创建者,负责创建和推送数据到消息服务器
消费者:消息的接收方,用于处理数据和确认消息
代理:就是RabbitMQ本身,扮演快递的角色,本身不生产消息,只是扮演快递的角色

22 消息的持久化是如何实现的

  1. exchange持久化,在声明时指定 durable=>1
  2. queue持久化,在声明时指定durable=>1
  3. 消息持久化,在投递时指定delivery_mode =>2 (1是非持久化)

23 如何保证消息不丢失

生产者开启事务或者发送方确认机制,交换机、队列、消息全部设置持久化,消费者开启消费确认机制

24 RabbitMq中的概念及解释

Broker:简单来说就是消息队列服务器实体。
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息队列载体,每个消息都会被投入到一个或多个队列。
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
producer:消息生产者,就是投递消息的程序。
consumer:消息消费者,就是接受消息的程序。
channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务

消息队列的一个典型使用过程大概如下:
(1)客户端连接到消息队列服务器,打开一个channel。
(2)客户端声明一个exchange,并设置相关属性。
(3)客户端声明一个queue,并设置相关属性。
(4)客户端使用routing key,在exchange和queue之间建立好绑定关系。
(5)客户端投递消息到exchange
exchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。

由上可以看出:消费端:需要将queue与exchange绑定,通过routing_key来实现规则绑定。生产端:将消息发送到exchange时附加上 routing_key 即可以了,exchange会根据上面的规则来决定放在哪个queue里。

25 RabbitMq消息什么时候需要持久化?

1.消息本身在publish的时候就要求消息写入磁盘
2.内存紧张,需要将部分内存中的消息转移到磁盘

26 RabbitMq消息什么时候会刷到磁盘?

  1. 写入文件前会有一个Buffer,大小为1M,数据在写入文件时,首先会写入到这个Buffer,如果Buffer已经写满了,则会将Buffer写入到文件
  2. 有个固定的刷盘时间:25ms,也就是不管Buffer满不满,每隔25ms,Buffer里的数据及未刷新到磁盘的文件内容必定会刷到磁盘
  3. 每次消息写入后,如果没有后续写入请求,则会直接将已写入的消息刷到磁盘,使用Erlang的reveive x after 0来实现,只要进程的信箱里没有消息,则产生一个timeout消息,而timeout会触发刷盘操作

27 如果让你写一个消息队列,该如何进行架构设计?说一下你的思路。

比如说这个消息队列系统,我们从以下几个角度来考虑一下:
首先这个 mq 得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下 kafka 的设计理念,broker -> topic -> partition,每个 partition 放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给 topic 增加 partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?

其次你得考虑一下这个 mq 的数据要不要落地磁盘吧?那肯定要了,落磁盘才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路。

其次你考虑一下你的 mq 的可用性啊?这个事儿,具体参考之前可用性那个环节讲解的 kafka 的高可用保障机制。多副本 -> leader & follower -> broker 挂了重新选举 leader 即可对外服务。

能不能支持数据 0 丢失啊?可以的,参考我们之前说的那个 kafka 数据零丢失方案。

在这里插入图片描述

28 如何解决消息积压

如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百分消息持续积压几个小时,说说怎么解决。

   这些问法,本质上都是针对场景,都是说可能你的消息端出来问题,不消费了。或者消费的及其满。接着就坑爹了。可能你的消息队列集群的磁盘都快满了。都没人消费,这个时候怎么办?或者是积压了几个小时,怎么办?或者是积压时间太长了,导致比如RabbitMQ设置了过期时间后就没了。其实这事,线上挺常见的,一般不出,一出就是大case。举个例子,消费端每次消费之后要写mysql,结果mysql挂了,消费端不动了,或者是消费端出了什么叉子,导致消费速度灰常慢。

   1、快速处理积压的消息

   (1)大量消息在MQ里积压几个小时,还没解决

    几千万条数据在MQ里,积压了七八个小时。这个时候就是恢复consumer的问题。让它恢复消费速度,然后傻傻地等待几个小时消费完毕。这个肯定不能再面试的时候说。1个消费者1秒时1000条,1秒3个消费者是3000条。1分钟是18万条。1个小时是1000多万条。如果积压了上万条数据,即使消费者恢复了,也大概需要1个多小时才能恢复过来。

在这里插入图片描述
原来3个消费者1个小时。现在30个消费者,需要10分钟搞定。
一般情况下,这个时候只能做临时扩容了。具体操作步骤和思路如下:

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

    ② 新建1个topic,partition是原来的10倍,临时建立好原来10倍或者20倍的Queue。

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

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

    ⑤ 这种做法,相当于将queue资源和consume资源扩大10倍,以10倍的速度来消费数据。

    ⑥ 等快速消费完积压数据之后,恢复原来的部署架构,重新用原先的consumer来消费消息。

  (2)过期失效了怎么办

    过期失效就是TTL。如果消息在Queue中积压超过一定的时间就会被RabbitMQ给清理掉。这个数据就没了。这就不是数据积压MQ中了,而是大量的数据会直接搞丢。

    在这种情况下,增加consume消费积压就不起作用了。此时,只能将丢失的那批数据,写个临时的程序,一点一点查出来,然后再灌入MQ中,把白天丢失的数据补回来。