消息队列面试题

1. 项目中为什么使用mq

(1) 解耦

  • 一个模块, 调用其他多个模块的接口, 调用过程很复杂, 但又不是必须同步调用的时候, 就可以将这个调用最为一条消息放在mq里, 让被调用者取订阅相关的消息, 此时被调用的那几个模块就可以从mq里得知有一个模块调用我了
  • 不用考虑对方调用是否成功, 超时, 失败重试等
  • 主要是应用了消息队列的pub-sub模型

(2) 异步

  • 不用mq的同步高延时场景:
    还是以上场景, 模块A要分别调用模块B, 模块C, 模块D; 假设BCD三个模块分别处理3条sql, 5条sql, 4条sql, 且分别耗时200ms, 250ms, 700ms, 则模块A要想正常返回, 需要1150ms, 这在用户体验上十分不可接受(一般可接受范围在200ms以内)
  • 消峰

2. 引入消息队列后, 有什么坏处

  • 系统可用性降低
  • 系统与MQ之间的交互可能会产生问题
    • 消息重复: 比如系统A本来只应该调用系统B一次, 但是由于系统A和MQ交互发生问题 ,使得A存了2此请求在mq中, 则B会被请求2次
    • 消息丢失: 少了几次请求
    • 消息乱序: 调用次序变了
    • 消费者挂掉了: 系统BCD挂掉了, 系统A狂往队列中加数据
  • 一致性问题
    本来系统ABCD都成在能返回给用户成功, 加入mq后, 调用变成异步的了, 恰恰BCD中某个模块失败了, 但A却已经给用户正确的返回了

3. 如何保证mq的高可用性

(1) Rabbit mq高可用的做法

  • 普通集群模式
    数据只保存在某1个节点上, 而其它节点只负责响应request请求, 然后查找该请求的数据在那个节点上, 把数据从其他节点传输到本节点再返回给用户端
    这种方式没有任何高可用性
  • 镜像集群模式(数据副本, n台机器保存n分完整数据, 不存在partition, 只存在replication)
    每个机器都是完整的queue的数据, 这几个节点的数据互为备份, 但是仍然不是分布式的queue, 只能用一台机器存储数据
    (通过管理平台 增加一条镜像管理策略, 配置所有机器同部数据/某几台机器同步数据)

(2) kafka高可用性的做法

  • 数据分区(partition)
  • 数据备份(每个partition分为leader和follower)

4. mq consumer如何保证消息不被重复消费or如何保证消息的幂等性

(1) kafka
每条消息有一个offset, 可理解为每个消息的一个编号. consumer消费后返回offset(基于zookeeper里记录消费到了哪个offset) 或记录到kafka的一个特定队列中(__consumer_offsets下面)

如何保证消息重复消费的幂等性
(1) 为什么会出现数据的重复消费
比如kafka中, 如果consumer在消费后就宕机了, 而且还没来得及到zookeeper中记录自己消费的offset
(2) 如何保证幂等性
比如consumer消费数据时要写入databse, 可以记录一个消息主键, 重复消费时会报错但不会重复保存

5. 如何处理使用mq后消息存在丢失的问题

  • rabbitmq
    (1) 生产者把数据发往mq的时候丢了

    1. rabbitmq可以通过开启事务实现避免生产者这端丢数据. 这种模式是同步机制, 只等这条消息发送完毕再发送下一条消息
      channel.txSelect()
      try{
        // 发送消息
      }catch (异常){
        channel.txrollback;
        // 再次重试发送数据
      }
      
    2. 或者开启confirm机制.
      • 先把channel设置为confirm机制,
      • 然后发送消息,
      • 发送成功后, rabbitmq接收消息后会回调生产者本地的回调接口, 如果mq保存消息成功回调ack()方法, 如果保存失败则回调unack()方法
    这种方法是异步机制, 发送完不用等待回调就可以立刻发送下一条. 一般选用confirm机制
    

    (2) mq存储的数据丢了

    • rabbitmq开启持久化
      • a. 先把queue的元数据开启持久化
      • b. 把diliverymode设置为2, 让queue里的数据持久化
        但此时仍存在一个风险就是, 数据存到内存后, 还没来得及存到磁盘, mq就挂了, 导致数据丢失

    (3) 消费者处理失败导致消息丢失
    消费者从mq收到消息后就挂掉了, 但还没来得及消费, 而mq却认为消费者取走就代表消费完毕
    这种一般是开启了消费者的auto ack机制, 这样会让rabbitmq认为收到消息就是处理完毕, 此时若在处理过程中consumer宕机, 就会出现数据丢失问题
    所以应该关闭autoack机制, 在consumer处理完消息后在手动进行ack

  • kafka
    (1) kafka存储的数据丢失
    因为kafka的partition通过leader和follower进行数据高可用, 有时数据只写到了leader的内存, 还没复制到follower时, leader就宕机了, 此后将follower选举成leader的时候数据就丢失了
    解决办法: 设置4个参数

    1. kafka服务期设置
      • 为topic设置replication.factors大于1,
        确保数据至少有2个副本
      • min.insync.replicats:
        kafka的leader会对follower进行心跳, 如果超时认为该follower宕机. 该参数必须大于等于2, 表示数据至少有1个follower可以和leader正常通信
    2. producer段设置
      • ack=all:
        每条数据必须写入所有的follower才认为是写入成功了
      • retries=MAX:
        一旦失败, 就自动无限重试的重发数据. 该参数设置后配合ack=all, 使得当leader未写成功所有follower就宕机后leader切换, producer可以再次把数据发送给新leader

    (2) kafka消费端丢数据
    原理一样. kafka的自动提交offset机制, 导致consumer收到消息成功但处理过程中失败时造成数据丢失. 解决办法也是关闭自动提交offset, 来手动提交offset

6. 如何保证消息的顺序性

(1) 什么时候rabbitMq的数据会出现顺序性问题
当producer只有1个, consumer有多个, 且mq以队列模式存在(每个consumer依次从mq中拿数据), 这种时候, 即使mq中的消息不乱序, 但由于consumer处理消息的速度不同, 最终产生结果的顺序会不同(比如consumer是插入数据到数据库的话)
解决办法: 需要保证顺序的消息都放到1个queue里, 同时只被1个consumer消费

(2) 什么时候kafka出现顺序问题

  1. kafka可以保证写入一个partition的数据时有序的, 所以可以在写入数据时指定一个key
producer.send(new ProducerRecord<>(topic,partition,key, msg));

比如: 设置订单id为key, 这个key相关的数据全都会发送到1个partition中

  1. kafka的另一个原则:
    1个partition只能被1个消费者消费. 即如果partition有3个, 而开启了4个consumer, 那有1个consumer会闲置
  2. 但是, 如果1个consumer内开启了多个线程处理数据, 则还会出现数据顺序的问题.
    解决办法:
    如果确实1个consumer需要多个线程并发加速处理数据, 那么可以为每个线程开启一个内存queue, 从kafka中得到数据时, 把key按照hash分发到不同的queue. 例如: 1个订单id的消息, 既可以保证生产者生产的数据在partition中有序, 也可以保证消费者在多线程下消费的顺序性

7. 有几百万消息积压在kafka消息队列怎么办

    消息积压是因为consumer出问题或下线了, 重启恢复consumer后, 首先创建1个新的topic, 让这个新的topic拥有的partition数量是原来的10倍, 修改原来的consumer代码逻辑为: 把老topic的数据消费后就存入新的topic. 然后再启动10倍的consumer消费这个新topic下的消息.
为什么不能直接启动10倍的consumer消费老topic?
    因为kafka里1个partition只能被1个consumer消费, 老topic的partition数量不变的话, 即使新增consumer, 那些consumer也会被闲置

8. 如果让你来开发一个mq中间件, 如何设计这个架构

面试官心理分析:
    其实聊到这个问题,一般面试官要考察两块:
你有没有对某一个消息队列做过较为深入的原理的了解,或者从整体了解把握住一个消息队列的架构原理。
看看你的设计能力,给你一个常见的系统,就是消息队列系统,看看你能不能从全局把握一下整体架构设计,给出一些关键点出来。
说实话,问类似问题的时候,大部分人基本都会蒙,因为平时从来没有思考过类似的问题,大多数人就是平时埋头用,从来不去思考背后的一些东西。类似的问题,比如,如果让你来设计一个 Spring 框架你会怎么做?如果让你来设计一个 Dubbo 框架你会怎么做?如果让你来设计一个 MyBatis 框架你会怎么做?
回答思路:
     首先, 让mq支持数据的分布式存储, 其次支持高可用(数据副本), 支持数据的零丢失(数据副本的一致性问题)

rabbitMQ基本概念

  • 概念:
    • Brocker:消息队列服务器实体。
    • Exchange:消息交换机,指定消息按什么规则,路由到哪个队列。
    • Queue:消息队列,每个消息都会被投入到一个或者多个队列里。
    • Binding:绑定,它的作用是把exchange和queue按照路由规则binding起来。
    • Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
    • Vhost:虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。
    • Producer:消息生产者,就是投递消息的程序。
    • Consumer:消息消费者,就是接受消息的程序。
    • Channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。
  • 消息队列的使用过程大概如下:
    • 消息接收
      • 客户端连接到消息队列服务器,打开一个channel。
      • 客户端声明一个exchange,并设置相关属性。
      • 客户端声明一个queue,并设置相关属性。
      • 客户端使用routing key,在exchange和queue之间建立好绑定关系。
    • 消息发布
      • 客户端投递消息到exchange。
      • xchange接收到消息后,就根据消息的key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里。
  • AMQP 里主要要说两个组件:
    Exchange 和 Queue
    绿色的 X 就是 Exchange ,红色的是 Queue ,这两者都在 Server 端,又称作 Broker
    这部分是 RabbitMQ 实现的,而蓝色的则是客户端,通常有 Producer 和 Consumer 两种类型。
  • Exchange通常分为四种:
    • fanout:该类型路由规则非常简单,会把所有发送到该Exchange的消息路由到所有与它绑定的Queue中,相当于广播功能
    • direct:该类型路由规则会将消息路由到binding key与routing key完全匹配的Queue中
    • topic:与direct类型相似,只是规则没有那么严格,可以模糊匹配和多条件匹配
    • headers:该类型不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配

kafka基本概念:

topic: 队列, 相当于rabbitmq的queue
partition: 数据的分片(分布式存储数据), rabbitmq中没有分布式存储的概念

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 148,637评论 1 318
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 63,443评论 1 266
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 99,164评论 0 218
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,075评论 0 188
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,080评论 1 266
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,365评论 1 184
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,901评论 2 283
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,649评论 0 176
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,122评论 0 223
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,734评论 2 225
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,093评论 1 236
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,548评论 2 222
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,028评论 3 216
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,765评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,291评论 0 178
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,162评论 2 239
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,293评论 2 242