Mq消息队列面试笔记

/ 中间件面试 / 没有评论 / 520浏览

为什么使用消息队列?

解耦,异步,削峰

缺点:降低系统的可用性(外部依赖越多,越容易挂掉)

系统复杂度提高

一致性问题

Kafka,RabbitMq,RocketMq的优缺点

特性 RabbitMq RocketMq Kafka
单机吞吐率 万级 10万级 10万(用于大数据实时计算,日志采集)
topic数量对应吞吐量 topic达到几千吞吐量下降幅度小 topic从几十到几百的时候吞吐量大幅度下降(增加更多机器资源)
时效性 微秒级,延迟最低 ms级 延迟在ms级以内
可用性 高,基于主从架构实现高可用 非常高,分布式架构 非常高,分布式,一个数据多个副本,少数宕机不会丢失数据,不可用
消息可靠性 基本不丢 经过优化配置,可以做到0丢失 经过优化配置,可以做到0丢失
功能支持 基于erlang开发,并发能力腔,性能好,延迟低 mq功能较为完善,分布式的,扩展性好 功能较简单,主要应用与大数据实时计算以及日志采集被大规模使用

1.如何选择消息队列

1.普遍使用rabbitMq,延迟低,高可用,性能好。但是因为erlang开发的,java开发人员接触不深。重在社区完善。不会黄

2.rocketMq目前已经捐献给了apache,但是目前活跃度不算高

3.kafka 主要还是应用于大数据领域

如何保证消息队列的高可用?

1.RabbitMq的高可用:主要基于主从实现系统高可用

不是分布式消息队列,是传统的消息队列,提供了一些高可用机制

单机模式,普通集群模式,镜像集群模式

单机模式:

**普通集群模式(无高可用性):**多个rabbitMq实例,每个实例都同步queue的元数据(配置信息,用于找对应实例)。每次随机拉去指定实例的数据

如果开启消息持久化,消息不一定会丢

镜像集群模式(高可用性):

后台管理系统里面开启, 会将queue元数据完整镜像到每台实例上面。缺点开销大

2.kafka的高可用性:

kafka:是由多个broker组成,每个broker是一个节点,创建一个topic这个topic会划分为多个partiton,每个partition存在与不同的broker上面,每个partition放一部分数据,天然的分布式消息队列。

kafka 0.8以后提供了Ha机制,replice(复制品)副本机制,每个protiton都会同步到其他机器上,形成自己的多个replice副本,所以replica会选举一个leader出来,与生产者和消费者打交道,其他replice就是follower,写的时候leader会负责吧数据写道folloer上面。读的时候直接的leader就行,只能读leader吗?要是随机读写任意follower,需要处理数据一致性问题。复杂度太高,容易出问题

ack机制,

写数据:生产者写leader,然后leader将数据逻辑写本地磁盘,然后follower 主动从leader来pull数据,一旦folloer写好数据会发送ack给leader,leader收到所有的follower的ack消息,就会返回成功给生产者(可以调整)

消费的时候,从leader去读取,只有当一个消息被所有follower都同步成功返回ack才能被读取到

如何保证消息的幂等性(如何保证消息不被重复消费)?

kafka:有个offset的概念,就是每个消息写进去,都有一个offset代表消费序号,然后consumer消费了数据之后,每隔一段时间会吧自己消费过的offset提交一下。但是如果遇到kill等宕机问题,没有提交offset就会重复销毁

token机制,version乐观锁机制,唯一键约束机制,基于redis校验

如何保证消息可靠性传说,或者说,如何处理消息的丢失问题

rabbitMq:

生产者弄丢数据 (网络问题)

1.(开启rabbitMq的事务,提过回滚提交),缺点:吞吐量不高

2.开启confirm模式,每次写消息都会分配一个唯一的id,如果写入rabbitMq成功后,会回传一个ack消息,如果没处理成功会回调nack接口

区别:事务机制是同步的,confirm模式是异步的

RabbitMq弄丢了数据

开启rabbitMq的持久化(设置queue元数据,和发送消息deliverMode设置为2)

消费端弄丢数据

提供ack机制,关闭自动ack。如果处理完毕后手动提交ack

kafka弄丢数据:

kafka的某个broker待机,然后重新选举partiton的leader,这个时候可能会丢失一条消息(上一个leader挂掉之前未能全部同步folloer)

避免:给topic设置多个副本。在服务端要求leader至少感知到一个follower跟自己保持联系,没有掉队。这样能确保leader挂了后还有一个follower还或者

将producer端设置成ack=all模式,这样保证每条数据写入到所有replica后才能算写成功

将producer端设置成retries=Max 这样一旦写入失败,就可以无限充实

生产者会不会弄丢数据?设置成ack=all 一定不会丢

如何保证消息的顺序性?

rabbitmq拆分成多个queue,每个queue对应一个consumer

kafka:使用单个partition

分组group概念

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

产生这种情况一般都是消费端出现了问题,不消费或者消费极慢

1:如果是系统并发不高的情况下,恢复消费端让他慢慢消费

2.是临时扩容,使用一个新的topic和partition 是原来的10倍,写一个分发消费者的程序去消费挤压的数据,

如果过期失效?

那么就批量重导数据,写一个临时程序在晚上批量灌入

一、KAFKA

是一个分布式,支持分区,多副本,基于zookeeper协调的分布式消息系统(通讯协议是Tcp

基本概念

Broker: 处理节点,一个kafka节点就是一个broker
Topic: kafka是根据Topic对消息分类的,发布到集群的消息都需要指定该tOpic
Producer:消息生产
Consumer:小肥猪
ConsumerGroup:消费者分组
Partition:一个topic可以分为多个分区,每个分区的内部消息是有序的
		

Partition
	- 是一个有序的message序列,这些message按顺序添加到commit log文件中,每个partition中的消息都有一个唯一的编号,称为offset,
	- 每个partition对应一个commit log文件。每个partition中的offset是唯一的,不同的partition的offset可能相同
	- 消费offset是由consumer自己来维护的,所以kafka集群是无状态的。性能不会因为consumer的过多而影响,kafka还将很多关键信息存储到zookeeper中,保证自己的无状态,从而水平扩展的时候方便

为什么对Topic下进行分区存储?

1.commit log文件系统大小的约束,理论一个topic可以处理任意数量的数据

2.为了提高并行度

分布式Distribution

commit log的partitions分布在kafka集群中不同的broker上,每个broker可以请求备份其他broker上partition上的数据。kafka 集群支持配置一个partition备份的数量。

针对每个partition,都有一个broker起到“leader”的作用,0个或多个其他的broker作为“follwers”的作用。

leader处理所有的针对这个partition的读写请求,而followers被动复制leader的结果。如果这个leader失效了,其中 的一个follower将会自动的变成新的leader。

Producers 生产者将消息发送到topic中去,同时负责选择将message发送到topic的哪一个partition中。通过round­robin做简单的 负载均衡。也可以根据消息中的某一个关键字来进行区分。通常第二种方式使用的更多。

Consumers 传统的消息传递模式有2种:队列( queue) 和(publish-subscribe)

queue模式:多个consumer从服务器中读取数据,消息只会到达一个consumer。所有的consumer都位于同一个consumer group 下。

publish-subscribe模式:消息会被广播给所有的consumer。所有的consumer都有着自己唯一的consumer group。

消费顺序

Kafka比传统的消息系统有着更强的顺序保证。 一个partition同一个时刻在一个consumer group中只有一个consumer instance在消费,从而保证顺序。 consumer group中的consumer instance的数量不能比一个Topic中的partition的数量多,否则,多出来的 consumer消费不到消息。

kafka的ack机制

producers 本地缓冲区(buffer->batch),默认32mb,默认每16kb批量发送一次,以及超时必须发送

Kafka核心总控制器Controller

在Kafka集群中会有一个或者多个broker,其中有一个broker会被选举为控制器(Kafka Controller),它负责管理整个 集群中所有分区和副本的状态。

Controller选举机制

在kafka集群启动的时候,会自动选举一台broker作为controller来管理整个集群,选举的过程是集群中每个broker都会 尝试在zookeeper上创建一个 /controller 临时节点,zookeeper会保证有且仅有一个broker能创建成功,这个broker 就会成为集群的总控器controller。 当这个controller角色的broker宕机了,此时zookeeper临时节点会消失,集群里其他broker会一直监听这个临时节 点,发现临时节点消失了,就竞争再次创建临时节点,就是我们上面说的选举机制,zookeeper又会保证有一个broker 成为新的controller。 具备控制器身份的broker需要比其他普通的broker多一份职责,

内容精炼

消息顺序消费:
    1. 指定同一个分区
    2. 使用相同的key(变相的使用同一个分区)
消息丢失:
    分为生产者、服务端、客户端三个方面
    生产者:
        设置异步发送,发送失败使用回调进行处理再发送
        失败重试,设置重试次数
    kafka服务端:
        发送机制设置ack,设置all,只有当所有副本保存成功之后才会认为成功
    消费者:
        分区的消息都有一个分区内的唯一偏移量 0 开始自增,各个分区的消息消费各有不同,所以再重平衡消费者之后,有可能导致消息丢失
        关闭自提交偏移量,开启手动提交偏移量,避免提交比较靠后的偏移量导致中间部分消息未被消费到
        提交方式改为 同步 + 异步方式
消息重复消费:
    唯一、幂等
高可用机制:
数据清理机制:
    1. 默认保存7天(168小时)
    2. topic存储数据的大小,如果大小超过一定阈值,就会删除从最早的数据开始删除
高性能设计:
    1. 消息分区,不受单节点限制,处理更多数据
    2. 顺序读写,磁盘的顺序读写,提升读写效率
    3. 页缓存,在内存中进行数据的操作,变磁盘访问为内存访问
    4. 零拷贝,减少中间态的IO复制,即上下文切换和数据拷贝
    5. 消息压缩,较少磁盘IO和网络IO
    6. 分批发送,将消息批量发送,减少网络IO的开销

在实际生产应用中,通常会使用kafka作为消息传输的数据管道,rabbitmq作为交易数据作为数据传输管道,主要的取舍因素则是是否存在丢数据的可能;
    rabbitmq在金融场景中经常使用,具有较高的严谨性,数据丢失的可能性更小,同事具备更高的实时性;
    而kafka优势主要体现在吞吐量上,虽然可以通过策略实现数据不丢失,但从严谨性角度来讲,大不如rabbitmq;而且由于kafka保证每条消息最少送达一次,有较小的概率会出现数据重复发送的情况;

https://blog.csdn.net/wanghaiping1993/article/details/125346010

kafka如何实现数据的高效读取?(顺序读写、分段命令、二分查找)
    Kafka为每个分段后的数据文件建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为index。
    index文件中并没有为数据文件中的每条Message建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。
    这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。

二、Rabbit MQ

elang语言开发,能做到微秒级别延迟

基本组件概念

基本概念

当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue 等。

死信队列

死信队列(DLX,Dead-Letter-Exchange),利用DLX,当消息在一个队列中变成无法被消费的消息(dead message)之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX。

1、 消息被拒绝(channel.basicReject/channel.basicNack)并且request=false;
2、 消息在队列的存活时间超过设置的生存时间(TTL)时间;
3、 队列达到最大长度(队列满了,无法再添加数据到队列中)。
DLX也是一个正常的Exchange,和一般的Exchange没有区别,它能在任何的队列上被指定,实际上就是设置某个队列的属性。

延迟插件实现原理

rabbitmq_delayed_message_exchange插件,实现延迟队列效果。 它是一种新的交换类型,该类型消息支持延迟投递机制消息传递后并不会立即投递到目标队列中,而是存储在mnesia(一个分布式数据系统)表中, 当达到投递时间时,才投递到目标队列中。使用延迟队列,可以有效解决定时任务带来的系统压力以及业务处理时效性等问题。

五大消息模型

基本消息模型:
  生产者将消息发送到队列,消费者从队列中获取消息,队列是存储消息的缓冲区。

work消息模型:
  让多个消费者绑定到一个队列,共同消费队列中的消息。队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的

订阅模型:
  1、1个生产者,多个消费者
  2、每一个消费者都有自己的一个队列
  3、生产者没有将消息直接发送到队列,而是发送到了交换机
  4、每个队列都要绑定到交换机
  5、生产者发送的消息,经过交换机到达队列,实现一个消息被多个消费者获取的目的

订阅模型-Fanout 广播
  1) 可以有多个消费者
  2) 每个消费者有自己的queue(队列)
  3) 每个队列都要绑定到Exchange(交换机)
  4) 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
  5) 交换机把消息发送给绑定过的所有队列
  6) 队列的消费者都能拿到消息。实现一条消息被多个消费者消费

订阅模型-Direct
  在 Direct 模型下,队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由 key),
                消息的发送方在向 Exchange 发送消息时,也必须指定消息的 routing key。
  P:生产者,向 Exchange 发送消息,发送消息时,会指定一个 routing key。
  X:Exchange(交换机),接收生产者的消息,然后把消息递交给 与 routing key 完全匹配的队列
  C1:消费者,其所在队列指定了需要 routing key 为 error 的消息
  C2:消费者,其所在队列指定了需要 routing key 为 info、error、warning 的消息

订阅模型-Topic
  Topic 类型的 Exchange 与 Direct 相比,都是可以根据 RoutingKey 把消息路由到不同的队列。
  只不过 Topic 类型 Exchange 可以让队列在绑定 Routing key 的时候使用通配符!
  通配符规则:#:匹配一个或多个词*:匹配不多不少恰好 1 个词

5种消息模式

1.简单模式:一个生产者,默认交换机,一个队列,一个消费者

2.工作模式:一个生产者,默认交换机,一个队列,多个消费者

3.发布订阅模式:一个生产者,一个交换机,多个队列,多个消费者

4.路由模式:一个生成者,一个交换机,多个队列,多个消费者,消息发到交换机,然后进行路由给对象的队列消费者

5.主题topic模式:

三、rocketMQ

其他问题

在实际生产应用中,通常会使用kafka作为消息传输的数据管道,rabbitmq作为交易数据作为数据传输管道,主要的取舍因素则是是否存在丢数据的可能;

rabbitmq在金融场景中经常使用,具有较高的严谨性,数据丢失的可能性更小,同事具备更高的实时性;

而kafka优势主要体现在吞吐量上,虽然可以通过策略实现数据不丢失,但从严谨性角度来讲,大不如rabbitmq;而且由于kafka保证每条消息最少送达一次,有较小的概率会出现数据重复发送的情况;

1.mq介绍及使用场景


    消息队列 messageQueue 先进先出队列
    优点:
    异步(减少响应时间)、解耦(分支)、削峰填谷、数据分发
    缺点:
    可用性减低(mq宕机,影响业务)、系统复杂度提高(数据链路变长,消息丢失、重复调用、 序性)、数据一致性

2.保证mq消息不丢失

造成消息丢失的原因:
    生产者丢失:
        消息发送成功确认回调机制
        rocketmq的事务消息:Producer端消息发送事件和本地事务事件,同时成功或同时失败
                正常事务的发送及提交、事务信息的补偿流程(都是针对生产者 因为事务只出现在DataBase中 有些情况需要将消息存储在数据库中 如果发生事务问题…)
        整体流程为:
            正常事务发送与提交阶段
            生产者发送一个半消息给broker(半消息是指的暂时不能消费的消息)
            服务端响应
            开始执行本地事务
            根据本地事务的执行情况执行Commit或者Rollback
            事务信息的补偿流程
            
            如果broker长时间没有收到本地事务的执行状态,会向生产者发起一个确认会查的操作请求
            生产者收到确认会查请求后,检查本地事务的执行状态
            根据检查后的结果执行Commit或者Rollback操作 补偿阶段主要是用于解决生产者在发送Commit或者Rollback操作时发生超时或失败的情况
    mq服务端丢失:
        主从节点同步数据丢失
            刷盘丢失(内存写入磁盘)
        消费者端丢失:

        如何避免消息丢失:
            生产者丢失:

保证消息顺序消费:
    全局有序和局部有序:mq 只需要保证局部有序,并不需要保证全局有序(无业务意义)
    rocketmq:有完整的设计(生产顺序性和消费顺序性)

保证消息消费的幂等性:
    mq 产品并没有提供主动解决幂等性的机制,需要由消费者自行控制
    rocketmq: 生产者给消息分配了一个 messageId,这个 messageId就是作为消费者判断幂等的依据,但是有可能全局不唯一
        在消息中手动设置一个 全局唯一标识,来进行幂等判断。