消息队列
1.消息队列有哪些应用场景?
异步处理、流量控制、服务解耦、消息广播
2.消息队列的弊端有哪些?
数据延迟;
增加系统复杂度;
可能产生数据不一致的问题。
3.使用消息队列,怎么确保消息不丢失?
在生产阶段,你需要捕获消息发送的错误,并重发消息。 在存储阶段,你可以通过配置刷盘和 复制相关的参数,让消息写入到多个副本的磁盘上,来确保消息不会因为某个 Broker 宕机或 者磁盘损坏而丢失。 在消费阶段,你需要在处理完全部消费业务逻辑之后,再发送消费确认。
4.使用消息队列,如果处理重复消息?
1)利用数据库的唯一约束实现幂等
2)为更新的数据设置前置条件(CAS)
3)记录并检查操 作(在发送消息时,给每条消息指定一个全局唯一的 ID,消费时,先根据这个 ID 检查这条消 息是否有被消费过,如果没有消费过,才更新数据,然后将消费状态置为已消费。)
5.Kafka的消息是有序的吗?如果保证Kafka消息的顺序性?
Kafka只能保证局部有序,即只能保证一个分区里的消息有序。而其具体实现是通过生产者为 每个分区的消息维护一个发送队列,我们需要将保证顺序的消息都发送到同一个分区中。并且 由于Kafka会同时发送多个消息,所以还需指定max.in.flight.requests.per.connection为 1,保证前一个消息发送成功,后一个消息才开始发送。
6.消息如何保证幂等性 ?
例如kafka的offset可能是消费者批量处理后才提交到zk,重启后再消费时就可能会收到重复 消息,需要消费者在处理消息时做幂等性设计,即先判断是否消费过,把已消费的放到本地缓 存或者redis中,每次消费时先做个判断即可。
7.消息队列积压怎么办
当消费者出现异常,很容易引起队列积压,如果一秒钟1000个消息,那么一个小时就是几千万 的消息积压,是非常可怕的事情,但是生产线上又有可能会出现; 当消息积压来不及处理,rabbitMQ如果设置了消息过期时间,那么就有可能由于积压无法及 时处理而过期,这消息就被丢失了;
解决方法如下:
- 不建议在生产环境使用数据过期策略,一是数据是否丢失无法控制,二是一旦积压 就很有可能丢失;建议数据的处理都有代码来控制;
- 当出现消息积压时,做法就是临时扩大consumer个数,让消息快速消费,一般都 是通过业务逻辑的手段来完成:
如下: 【rabbitmq解决积压范例】
- 修复consumer代码故障,确保consumer逻辑正确可以消费;
- 停止consumer,开启10倍20倍的queue个数;
- 创建一个临时的consumer程序,消费积压的queue,并把消息写入到扩建 的10倍queue中;
- 再开启10倍20倍的consumer对新的扩充后队列进行消费; * 这种做法相当于通过物理资源扩充了10倍来快速消费;
- 当消费完成后,需要恢复原有架构,开启原来的consumer进行正常消费;
- 【kafka解决范例】
- 修复consumer代码故障,确保consumer逻辑正确可以消费;
- 停止consumer,新建topic,新建10倍20倍的partition个数;
- 创建对应原topic的partition个数的临时的consumer程序,消费原来的 topic,并把消息写入到扩建的新topic中;
- 再开启对应新partition个数的consumer对新的topic进行消费;
- 这种做法相当于通过物理资源扩充了10倍来快速消费; 1. 当消费完成后,需要恢复原有架构,开启原来的consumer进行正常消费;
8.各种MQ的比较
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EmehKBB9-1677917399220)(消息队列.assets/image-20230227165033967.png)]
【activeMQ】
优点:技术成熟,功能齐全,历史悠久,有大量公司在使用
缺点:偶尔会有较低概率丢失数据,而且社区已经不怎么维护5.15.X版本 使用场景:主要用于系统解耦和异步处理,不适用与大数据量吞吐情况。互联网公 司很少适用
【rabitMQ】
优点:吞吐量高,功能齐全,管理界面易用,社区活跃,性能极好,;
缺点:吞吐量只是万级,erlang难以二次开发和掌控;集群动态扩展非常麻烦; 使用场景:吞吐量不高而要求低延迟,并且不会频繁调整和扩展的场景。非常适合 国内中小型互联网公司适用,因为管理界面非常友好,可以在界面进行配置和优 化/集群监控。
【rocketMQ】
优点:支持百千级大规模topic。吞吐量高(十万级,日处理上百亿)。接口易 用。,分布式易扩展,阿里支持。java开发易于掌控。
缺点:与阿里(社区)存在绑定。不兼容JMS规范。 使用场景:高吞吐量
【kafka】
优点:超高吞吐量,超高可用性和可靠性,分布式易扩展
缺点:topic支持少,MQ功能简单,消息可能会重复消费影响数据精确度 使用场景:超高吞吐量场景而数据精确度没那么高,天然适合大数据实时计算和日 志采集场景
9.如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有 几百万消息持续积压几小时怎么解决?
(一)、大量消息在mq里积压了几个小时了还没解决
几千万条数据在MQ里积压了七八个小时,从下午4点多,积压到了晚上很晚,10点多,11点多这个是我们真实遇到过的一个场景,确实是线上故障了,这个时候要不然就是修复consumer的问题,让他恢复消费速度,然后傻傻的等待几个小时消费完毕。这个肯定不能在面试的时候说吧。
一个消费者一秒是1000条,一秒3个消费者是3000条,一分钟是18万条,1000多万条,所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概1小时的时间才能恢复过来。一般这个时候,只能操作临时紧急扩容了,具体操作步骤和思路如下:
-
先修复consumer的问题,确保其恢复消费速度,然后将现有cnosumer都停掉。
-
新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量。
-
然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue。
-
接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据。
-
这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据。等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息。
(二)、消息队列过期失效问题
假设你用的是rabbitmq,rabbitmq是可以设置过期时间的,就是TTL,如果消息在queue中
积压超过一定的时间就会被rabbitmq给清理掉,这个数据就没了。那这就是第二个坑了。这
就不是说数据会大量积压在mq里,而是大量的数据会直接搞丢。
这个情况下,就不是说要增加consumer消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。
这个时候我们就开始写**程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入mq里面去,**把白天丢的数据给他补回来。也只能是这样了。假设1万个订单积压在mq里面,没有处理,其中1000个订单都丢了,你只能手动写程序把那1000个订单给查出来,手动发到mq里去再补一次。
((三)、消息队列满了怎么搞?
如果走的方式是消息积压在mq里,那么如果你很长时间都没处理掉,此时导致mq都快写满 了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接 入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后走第二个方案, 到了晚上再补数据吧。
10.为什么使用消息队列?
解耦
看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个 数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃…
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k0VbtQYp-1677917399222)(消息队列.assets/image-20230227170435027.png)]
在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊!
如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,**也不需要考虑人家是否调用成功、失败超时等**情况。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dDWoRjVL-1677917399222)(消息队列.assets/image-20230227170549595.png)]
总结:通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦 了。 3
面试技巧:你需要去考虑一下你负责的系统中是否有类似的场景,就是一个系统或者一个模 块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用 是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦,也是可以的,你就需要去考虑在 你的项目里,是不是可以运用这个 MQ 去进行系统的解耦。在简历中体现出来这块东西,用 MQ 作解耦。
异步
再来看一个场景,A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写 库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求 总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。 用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MnDYqads-1677917399223)(消息队列.assets/image-20230227170728020.png)]
一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完
成,对用户几乎是无感知的。如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了,爽!网站做得真好,真快
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rbTlyrvJ-1677917399223)(消息队列.assets/image-20230227171102609.png)]
削峰
kafka.
5.3.2 和其他消息队列相⽐,Kafka的优势在哪⾥?
我们现在经常提到 Kafka 的时候就已经默认它是⼀个⾮常优秀的消息队列了,我们也会经常拿它给 RocketMQ、RabbitMQ 对⽐。我觉得 Kafka 相⽐其他消息队列主要的优势如下:
- 极致的性能 :基于 Scala 和 Java 语⾔开发,设计中⼤量使⽤了批量处理和异步的思想,最
⾼可以每秒处理千万级别的消息。 - ⽣态系统兼容性⽆可匹敌 :Kafka 与周边⽣态系统的兼容性是最好的没有之⼀,尤其在⼤数
据和流计算领域。
实际上在早期的时候 Kafka 并不是⼀个合格的消息队列,早期的 Kafka 在消息队列领域就像是⼀个⾐衫褴褛的孩⼦⼀样,功能不完备并且有⼀些⼩问题⽐如丢失消息、不保证消息可靠性等等。当然,这也和 LinkedIn 最早开发 Kafka ⽤于处理海量的⽇志有很⼤关系,哈哈哈,⼈家本来最开始就不是为了作为消息队列滴,谁知道后⾯误打误撞在消息队列领域占据了⼀席之地。随着后续的发展,这些短板都被 Kafka 逐步修复完善。所以,Kafka 作为消息队列不可靠这个说法已经过时!
5.3.4 什么是Producer、Consumer、Broker、Topic、Partition?
Kafka 将⽣产者发布的消息发送到 Topic(主题) 中,需要这些消息的消费者可以订阅这些Topic(主题),如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5iLCDR9G-1677917399224)(消息队列.assets/image-20230227172801488.png)]
上⾯这张图也为我们引出了,Kafka ⽐重要的⼏个概念:
- Producer(⽣产者) : 产⽣消息的⼀⽅。
- Consumer(消费者) : 消费消息的⼀⽅。
- Broker(代理) : 可以看作是⼀个独⽴的 Kafka 实例。多个 Kafka Broker 组成⼀个 KafkaCluster。
同时,你⼀定也注意到每个 Broker 中⼜包含了 Topic 以及 Partition 这两个重要的概念:
- Topic(主题) : Producer 将消息发送到特定的主题,Consumer 通过订阅特定的 Topic(主题) 来消费消息。
- Partition(分区) : **Partition 属于 Topic 的⼀部分。⼀个 Topic 可以有多个 Partition ,并且同⼀ Topic 下的 Partition 可以分布在不同的 Broker 上**,这也就表明⼀个 Topic 可以横跨多个 Broker 。这正如我上⾯所画的图⼀样。
5.3.5 Kafka 的多副本机制了解吗?带来了什么好处?
还有⼀点我觉得⽐重要的是 Kafka 为分区(Partition)引⼊了多副本(Replica)机制。分区(Partition)中的多个副本之间会有⼀个叫做 leader 的家伙,其他副本称为 follower。我们发送的消息会被发送到 leader 副本,然后 follower 副本才能从 leader 副本中拉取消息进⾏同步。
⽣产者和消费者只与 leader 副本交互。你可以理解为其他副本只是 leader 副本的拷⻉,它们的存在只是为了保证消息存储的安全性。当 leader 副本发⽣故障时会从 follower 中选举出⼀个 leader,但是 follower 中如果有和 leader 同步程度达不到要求的参加不了 leader 的竞选。
Kafka 的多分区(Partition)以及多副本(Replica)机制有什么好处呢?
- Kafka 通过给特定 Topic 指定多个 Partition, ⽽各个 Partition 可以分布在不同的 Broker 上,
这样便能提供⽐好的并发能⼒(负载均衡)。 - Partition 可以指定对应的 Replica 数, 这也极⼤地提⾼了消息存储的安全性, 提⾼了容灾能⼒,不过也相应的增加了所需要的存储空间
5.3.6 Zookeeper 在 Kafka 中的作⽤知道吗?
从图中我们可以看出,Zookeeper 主要为 Kafka 做了下⾯这些事情:
- Broker 注册 :在 Zookeeper 上会有⼀个专⻔⽤来进⾏ Broker 服务器列表记录的节点。每个 Broker 在启动时,都会到 Zookeeper 上进⾏注册,即到/brokers/ids 下创建属于⾃⼰的节点。每个 Broker 就会将⾃⼰的 IP 地址和端⼝等信息记录到该节点中去
- Topic 注册 : 在 Kafka 中,同⼀个Topic 的消息会被分成多个分区并将其分布在多个Broker 上,这些分区信息及与 Broker 的对应关系也都是由 Zookeeper 在维护。⽐如我创建了⼀个名字为 my-topic 的主题并且它有两个分区,对应到 zookeeper 中会创建这些⽂件夹: /brokers/topics/my-topic/Partitions/0 、 /brokers/topics/my-topic/Partitions/1
- 负载均衡 :上⾯也说过了 Kafka 通过给特定 Topic 指定多个 Partition, ⽽各个 Partition 可以分布在不同的 Broker 上, 这样便能提供⽐好的并发能⼒。 对于同⼀个 Topic 的不同Partition,Kafka 会尽⼒将这些 Partition 分布到不同的 Broker 服务器上。当⽣产者产⽣消息后也会尽量投递到不同 Broker 的 Partition ⾥⾯。当 Consumer 消费的时候,Zookeeper 可以根据当前的 Partition 数量以及 Consumer 数量来实现动态负载均衡。
5.3.7 Kafka 如何保证消息的消费顺序?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcj9bBXU-1677917399225)(消息队列.assets/image-20230227175100498.png)]
每次添加消息到 Partition(分区) 的时候都会采⽤尾加法,如上图所示。Kafka 只能为我们保证Partition(分区) 中的消息有序,⽽不能保证 Topic(主题) 中的 Partition(分区) 的有序。
消息在被追加到 Partition(分区)的时候都会分配⼀个特定的偏移量(offset)。Kafka 通过偏移量(offset)来保证消息在分区内的顺序性。所以,我们就有⼀种很简单的保证消息消费顺序的⽅法:1 个 Topic 只对应⼀个 Partition。这样当然可以解决问题,但是破坏了 Kafka 的设计初衷。
Kafka 中发送 1 条消息的时候,可以指定 topic, partition, key,data(数据) 4 个参数。如果你发送消息的时候指定了 Partition 的话,所有消息都会被发送到指定的 Partition。并且,同⼀个 key的消息可以保证只发送到同⼀个 partition,这个我们可以采⽤表/对象的 id 来作为 key 。
总结⼀下,对于如何保证 Kafka 中消息消费的顺序,有了下⾯两种⽅法:
- 1 个 Topic 只对应⼀个 Partition。
- (推荐)发送消息的时候指定 key/Partition。
5.3.8 Kafka 如何保证消息不丢失
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RcnxMPcz-1677917399225)(消息队列.assets/image-20230227204307067.png)]
2.Kafka中的ISR、AR又代表什么?ISR的伸缩又指什么?
ISR:In-Sync Replicas 副本同步队列
AR:Assigned Replicas 所有副本
ISR是由leader维护,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中==。AR=ISR+OSR。==
6.什么情况下一个 broker 会从 ISR 中被踢出去?
leader会维护一个与其基本保持同步的Replica列表,该列表称为ISR(in-sync Replica),每个Partition都会有一个ISR,而且是由leader动态维护 ,如果一个follower比一个leader落后太多,或者超过一定时间未发起数据复制请求,则leader将其重ISR中移除 。
7.kafka 为什么那么快?
Cache Filesystem Cache PageCache缓存顺序。 由于现代的操作系统提供了预读和写技术,磁盘的顺序写大多数情况下比随机写内存还要快。
Zero-copy。零拷技术减少拷贝次数
Batching of Messages 批量量处理。合并小的请求,然后以流的方式进行交互,直顶网络上限。
Pull 拉模式。使用拉模式进行消息的获取消费,与消费端处理能力相符。
9.kafka producer 打数据,ack 为 0, 1, -1 的时候代表啥, 设置 -1 的时候,什么情况下,leader 会认为一条消息 commit 了
- 1(默认):数据发送到Kafka后,经过leader成功接收消息的的确认,就算是发送成功了。在这种情况下,如果leader宕机了,则会丢失数据。
- 0:生产者将数据发送出去就不管了,不去等待任何返回。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
- -1:producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了
18.什么是消费者组?
消费者组是 Kafka 独有的概念,如果面试官问这 个,就说明他对此是有一定了解的。我先给
出标准答案:
1、定义:即消费者组是 Kafka 提供的可扩展且具有容错性的消费者机制。
2、原理:在 Kafka 中,消费者组是一个由多个消费者实例 构成的组。多个实例共同订阅若干个主题,实现共同消费。同一个组下的每个实例都配置有 相同的组 ID,被分配不同的订阅分区。当某个实例挂掉的时候,其他实例会自动地承担起 它负责消费的分区。此时,又有一个小技巧给到你:消费者组的题目,能够帮你在某种程度上掌控下面的面试方向。
如果你擅长位移值原理,就不妨再提一下消费者组的位移提交机制;
如果你擅长 Kafka Broker,可以提一下消费者组与 Broker 之间的交互;
如果你擅长与消费者组完全不相关的 Producer,那么就可以这么说:“消费者组要消 费的数据
完全来自于 Producer 端生产的消息,我对 Producer 还是比较熟悉的。”3.
19.解释下 Kafka 中位移(offset)的作用。
在 Kafka 中,每个 主题分区下的每条消息都被赋予了一个唯一的 ID 数值,用于标识它在分区中的位置。这个 ID 数值,就被称为位移,或者叫偏移量。一旦消息被写入到分区日志,它的位移值将不能 被修改。
答完这些之后,你还可以把整个面试方向转移到你希望的地方。常见方法有以下 3 种:如果你深谙 Broker 底层日志写入的逻辑,可以强调下消息在日志中的存放格式;如果你明白位移值一旦被确定不能修改,可以强调下“Log Cleaner 组件都不能影响位 移值”这件事情;如果你对消费者的概念还算熟悉,可以再详细说说位移值和消费者位移值之间的区别。
26.LEO、LSO、AR、ISR、HW 都表示什么含义?
ID 数值,就被称为位移,或者叫偏移量。一旦消息被写入到分区日志,它的位移值将不能 被修改。
答完这些之后,你还可以把整个面试方向转移到你希望的地方。常见方法有以下 3 种:如果你深谙 Broker 底层日志写入的逻辑,可以强调下消息在日志中的存放格式;如果你明白位移值一旦被确定不能修改,可以强调下“Log Cleaner 组件都不能影响位 移值”这件事情;如果你对消费者的概念还算熟悉,可以再详细说说位移值和消费者位移值之间的区别。
26.LEO、LSO、AR、ISR、HW 都表示什么含义?