为您推荐:
精华内容
最热下载
问答
  • 5星
    8.2MB Yao__Shun__Yu 2021-08-11 16:32:43
  • 5星
    19.58MB Yao__Shun__Yu 2021-05-31 15:44:57
  • 消息的顺序性 前 言 本文仅收录了一些常见的消息中间件面试题,如需查看其它java面试题可查看我另一篇博文: JAVA | 2021最全Java面试题及答案汇总 正 文 1. RabbitMQ 中的 broker 是指什么?cluster 又是 指什么?...

    文章目录


    前    言
    本文仅收录了一些常见的消息中间件面试题,如需查看其它java面试题可查看我另一篇博文:

    JAVA | 2021最全Java面试题及答案汇总


    正    文

    1. RabbitMQ 中的 broker 是指什么?cluster 又是 指什么?

    答:broker 是指一个或多个 erlang node 的逻辑分组,且 node 上运行着 RabbitMQ
    应用程序。cluster 是在 broker 的基础之上,增加了 node 之间共享元数据的约束。

    2. RabbitMQ 中 RAM node 和 disk node 的区别?

    答:RAM node 仅将 fabric(即 queue、exchange 和 binding等 RabbitMQ基础构件)
    相关元数据保存到内存中,但 disk node 会在内存和磁盘中均进行存储。RAM node 上唯一
    会存储到磁盘上的元数据是 cluster 中使用的 disk node 的地址。要求在 RabbitMQ
    cluster 中至少存在一个 disk node 。

    3. RabbitMQ 上的一个 queue 中存放的 message 是 否有数量限制?

    答:可以认为是无限制,因为限制取决于机器的内存,但是消息过多会导致处理效率的
    下降。

    4. RabbitMQ 概念里的 channel、exchange 和 queue 这些东东是逻辑概念,还是对应着进程实体?它们 分别起什么作用?

    答:queue 具有自己的 erlang 进程;exchange 内部实现为保存 binding 关系的查找
    表;channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给
    queue 。由 AMQP 协议描述可知,channel 是真实 TCP 连接之上的虚拟连接,所有 AMQP 命
    令都是通过 channel 发送的,且每一个 channel 有唯一的 ID。一个 channel 只能被单独
    一个操作系统线程使用,故投递到特定 channel 上的 message 是有顺序的。但一个操作系
    统线程上允许使用多个 channel 。channel 号为 0 的 channel 用于处理所有对于当前
    connection 全局有效的帧,而 1-65535 号 channel 用于处理和特定 channel 相关的帧。
    AMQP 协议给出的 channel 复用模型如下
    其中每一个 channel 运行在一个独立的线程上,多线程共享同一个 socket。

    5. RabbitMQ 中 vhost 是什么?起什么作用?

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

    6.在单 node 系统和多 node 构成的 cluster 系统 中声明 queue、exchange ,以及进行 binding 会 有什么不同?

    答:当你在单 node 上声明 queue 时,只要该 node 上相关元数据进行了变更,你就
    会得到 Queue.Declare-ok 回应;而在 cluster 上声明 queue ,则要求 cluster 上的全
    部 node 都要进行元数据成功更新,才会得到 Queue.Declare-ok 回应。另外,若 node 类
    型为 RAM node 则变更的数据仅保存在内存中,若类型为 disk node 则还要变更保存在磁
    盘上的数据。

    7.客户端连接到 cluster 中的任意 node 上是否都 能正常工作?

    答:是的。客户端感觉不到有何不同。

    8.能够在地理上分开的不同数据中心使用 RabbitMQ cluster 么?

    答:不能。第一,你无法控制所创建的 queue 实际分布在 cluster 里的哪个 node 上
    (一般使用 HAProxy + cluster 模型时都是这样),这可能会导致各种跨地域访问时的常
    见问题;第二,Erlang 的 OTP 通信框架对延迟的容忍度有限,这可能会触发各种超时,导
    致业务疲于处理;第三,在广域网上的连接失效问题将导致经典的“脑裂”问题,而
    RabbitMQ 目前无法处理(该问题主要是说 Mnesia)。

    9. routing_key 和 binding_key 的最大长度是多 少?

    答:255 字节。

    10. RabbitMQ 中 “dead letter”queue 有什么用 途?

    答:当消息被 RabbitMQ server 投递到 consumer 后,但 consumer 却通过
    Basic.Reject 进行了拒绝时(同时设置 requeue=false),那么该消息会被放入“dead
    letter”queue 中。该 queue 可用于排查 message 被 reject 或 undeliver 的原因。

    11. RabbitMQ 中 Basic.Reject 的用法是什么?

    答:该信令可用于 consumer 对收到的 message 进行 reject 。若在该信令中设置
    requeue=true,则当 RabbitMQ server 收到该拒绝信令后,会将该 message 重新发送到下
    一个处于消费状态的消费者处(理论上仍可能将该消息发送给当前 consumer)。若设置
    requeue=false ,则 RabbitMQ server 在收到拒绝信令后,将直接将该 message 从 queue
    中移除。
    而 Basic.Nack 是对 Basic.Reject 的扩展,以支持一次拒绝多条 message 的能力。

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

    答:首先,必然导致性能的下降,因为写磁盘比写 RAM 慢的多,message 的吞吐量可
    能有 10 倍的差距。
    其次,message 的持久化机制用在 RabbitMQ 的内置 cluster 方案时会出现问题。
    矛盾点在于,若消息设置了 persistent 属性,但 queue 未设置 durable 属性,那么
    当该 queue 的所属节点出现异常后,在未重建该queue前,发往该 queue 的 消息将被
    blackholed;若 消息设置了 persistent 属性,同时 queue 也设置了 durable 属性,那
    么当 queue 的所属节点异常且无法重启的情况下,则该 queue 无法在其他节点上重建,只
    能等待其所属节点重启后,才能恢复该 queue 的使用,而在这段时间内发送给该 queue 的
    message 将被 blackholed 。
    所以,是否要对 message 进行持久化,需要综合考虑性能需要,以及可能遇到的问题。
    若想达到 100,000 条/秒以上的消息吞吐量(单 RabbitMQ 服务器),则要么使用其他的方
    式来确保 message 的可靠 delivery ,要么使用非常快速的存储系统以支持全持久化(例
    如使用 SSD)。
    另外一种处理原则是:仅对关键消息作持久化处理(根据业务重要程度),且应该保证
    关键消息的量不会导致性能瓶颈。

    13. RabbitMQ 中的 cluster、mirrored queue 机制 分别用于解决什么问题?存在哪些问题?

    答:cluster 是为了解决当 cluster 中的任意 node 失效后,producer 和 consumer
    均可以通过其他 node 继续工作,即提高了可用性;另外可以通过增加 node 数量增加
    cluster 的消息吞吐量的目的。cluster 本身不负责 message 的可靠性问题(该问题由
    producer 通过各种机制自行解决);cluster 无法解决跨数据中心的问题(即脑裂问题)。
    另外,在cluster 前使用 HAProxy 可以解决 node 的选择问题,即业务无需知道 cluster
    中多个 node 的 ip 地址。可以利用 HAProxy 进行失效 node 的探测,可以作负载均衡。
    下图为 HAProxy + cluster 的模型。
    Mirrored queue 是为了解决使用 cluster 时所创建的 queue 的完整信息仅存在于单
    一 node 上的问题,从另一个角度增加可用性。

    14. 请概要说明 Kafka 的体系结构

    Kafka将消息以topic为单位进行归纳
    将向Kafka topic发布消息的程序成为producers.
    将预订topics并消费消息的程序成为consumer.
    Kafka以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个broker.
    producers通过网络将消息发送到Kafka集群,集群向消费者提供消息

    15. Kafa consumer 是否可以消费指定分区消息?

    Kafa consumer消费消息时,向broker发出"fetch"请求去消费特定分区的消息,
    consumer指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,
    customer拥有了offset的控制权,可以向后回滚去重新消费之前的消息。

    16. Kafka 消息是采用 Pull 模式,还是 Push 模式?

    Kafka最初考虑的问题是,customer应该从brokes拉取消息还是brokers将消息推送到
    consumer,也就是pull还push。在这方面,Kafka遵循了一种大部分消息系统共同的传统的
    设计:producer将消息推送到broker,consumer从broker拉取消息
    采用push模式,将消息推送到下游的consumer。这样做有好处也有坏处:由broker决定
    消息推送的速率,对于不同消费速率的consumer就不太好处理了。消息系统都致力于让
    consumer以最大的速率最快速的消费消息,但不幸的是,push模式下,当broker推送的速率
    远大于consumer消费的速率时,consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull
    模式。
    Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Push
    模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还
    是缓存之后批量推送。如果为了避免consumer崩溃而采用较低的推送速率,将可能导致一次
    只推送较少的消息而造成浪费。Pull模式下,consumer就可以根据自己的消费能力去决定这
    些策略
    Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,
    直到新消息到达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达。

    17. Kafka 高效文件存储设计特点:

    (1).Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容
    易定期清除或删除已经消费完文件,减少磁盘占用。
    (2).通过索引信息可以快速定位message和确定response的最大大小。
    (3).通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
    (4).通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。

    18. Kafka 与传统消息系统之间有三个关键区别

    (1).Kafka 持久化日志,这些日志可以被重复读取和无限期保留
    (2).Kafka 是一个分布式系统:它以集群的方式运行,可以灵活伸缩,在内部通过复制
    数据提升容错能力和高可用性
    (3).Kafka 支持实时的流式处理

    19. Kafka 里 partition 的数据如何保存到硬盘

    topic中的多个partition以文件夹的形式保存到broker,每个分区序号从0递增,且消
    息有序 。
    Partition文件下有多个segment(xxx.index,xxx.log)
    segment 文件里的 大小和配置文件大小一致可以根据要求修改 默认为1g
    如果大小大于1g时,会滚动一个新的segment并且以上一个segment最后一条消息的偏移
    量命名

    20. 请概述 kafka 的 ack 机制

    request.required.acks有三个值 0、 1、 -1
    0:生产者不会等待broker的ack,这个延迟最低但是存储的保证最弱当server挂掉的时
    候就会丢数据
    1:服务端会等待ack值 leader副本确认接收到消息后发送ack但是如果leader挂掉后他
    不确保是否复制完成新leader也会导致数据丢失
    -1:同样在1的基础上 服务端会等所有的follower的副本受到数据后才会受到leader
    发出的ack,这样数据不会丢失

    21. kafaka 生产数据时数据的分组策略

    生产者决定数据产生到集群的哪个partition中,每一条消息都是以(key,value)格
    式,Key是由生产者发送数据传入。所以生产者(key)决定了数据产生到集群的哪个
    partition。

    22. 数据传输的事务定义有哪三种?

    数据传输的事务定义通常有以下三种级别:
    (1)最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输
    (2)最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.
    (3)精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被一
    次而且仅仅被传输一次,这是大家所期望的

    23. 为什么使用消息队列?

    其实就是问问你消息队列都有哪些使用场景,然后你项目里具体是什么场景,说说你在
    这个场景里用消息队列是什么?
    面试官问你这个问题,期望的一个回答是说,你们公司有个什么业务场景,这个业务场
    景有个什么技术挑战,如果不用 MQ 可能会很麻烦,但是你现在用了 MQ 之后带给了你很多的
    好处。消息队列的常见使用场景,其实场景有很多,但是比较核心的有 3 个:解耦、异步、
    削峰。
    解耦:
    A 系统发送个数据到 BCD 三个系统,接口调用发送,那如果 E 系统也要这个数据呢?那
    如果 C 系统现在不需要了呢?现在 A 系统又要发送第二种数据了呢?而且 A 系统要时时刻刻
    考虑 BCDE 四个系统如果挂了咋办?要不要重发?我要不要把消息存起来?
    你需要去考虑一下你负责的系统中是否有类似的场景,就是一个系统或者一个模块,调
    用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不
    需要直接同步调用接口的,如果用 MQ 给他异步化解耦,也是可以的,你就需要去考虑在你
    的项目里,是不是可以运用这个 MQ 去进行系统的解耦。
    异步:
    A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写
    库要 30ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 30 + 300 +
    450 + 200 = 980ms,接近 1s,异步后,BCD 三个系统分别写库的时间,A 系统就不再考虑
    了。
    削峰:
    每天 0 点到 16 点,A 系统风平浪静,每秒并发请求数量就 100 个。结果每次一到 16 点
    ~23 点,每秒并发请求数量突然会暴增到 1 万条。但是系统最大的处理能力就只能是每秒钟
    处理 1000 个请求啊。怎么办?需要我们进行流量的削峰,让系统可以平缓的处理突增的请
    求。

    24. 消息队列有什么优点和缺点?

    优点上面已经说了,就是在特殊场景下有其对应的好处,解耦、异步、削峰。
    缺点呢?
    系统可用性降低
    系统引入的外部依赖越多,越容易挂掉,本来你就是 A 系统调用 BCD 三个系统的接口就
    好了,ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了怎么办?MQ 挂了,
    整套系统崩溃了,业务也就停顿了。
    系统复杂性提高
    硬生生加个 MQ 进来,怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保
    证消息传递的顺序性?
    一致性问题
    A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD
    三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,你这数据就不一致了。
    所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来
    的坏处做各种额外的技术方案和架构来规避掉。

    25. 消息重复的原因和解决

    第一类原因
    消息发送端应用的消息重复发送,有以下几种情况。

    • 消息发送端发送消息给消息中间件,消息中间件收到消息并成功存储,而 这时消息中间件出现了问题,导致应用端没有收到消息发送成功的返回因而进行重
      试产生了重复。
    • 消息中间件因为负载高响应变慢,成功把消息存储到消息存储中后,返回 “成功”这个结果时超时。
    • 消息中间件将消息成功写入消息存储,在返回结果时网络出现问题,导致 应用发送端重试,而重试时网络恢复,由此导致重复。

    可以看到,通过消息发送端产生消息重复的主要原因是消息成功进入消息存储后,因为
    各种原因使得消息发送端没有收到“成功”的返回结果,并且又有重试机制,因而导致重复。

    第二类原因
    消息到达了消息存储,由消息中间件进行向外的投递时产生重复,有以下几种情况。
    消息被投递到消息接收者应用进行处理,处理完毕后应用出问题了,消息中间件不知道
    消息处理结果,会再次投递。
    消息被投递到消息接收者应用进行处理,处理完毕后网络出现问题了,消息中间件没有
    收到消息处理结果,会再次投递。
    消息被投递到消息接收者应用进行处理,处理时间比较长,消息中间件因为消息超时会
    再次投递。
    消息被投递到消息接收者应用进行处理,处理完毕后消息中间件出问题了,没能收到消
    息结果并处理,会再次投递
    消息被投递到消息接收者应用进行处理,处理完毕后消息中间件收到结果但是遇到消息
    存储故障,没能更新投递状态,会再次投递。
    可以看到,在投递过程中产生的消息重复接收主要是因为消息接收者成功处理完消息后,
    消息中间件不能及时更新投递状态造成的。
    如何解决重复消费
    那么有什么办法可以解决呢?主要是要求消息接收者来处理这种重复的情况,也就是要
    求消息接收者的消息处理是幂等操作。
    什么是幂等性?
    对于消息接收端的情况,幂等的含义是采用同样的输入多次调用处理函数,得到同样的
    结果。例如,一个 SQL 操作
    update stat_table set count= 10 where id =1
    这个操作多次执行,id 等于 1 的记录中的 count 字段的值都为 10,这个操作就是幂等的,
    我们不用担心这个操作被重复。
    再来看另外一个 SQL 操作
    update stat_table set count= count +1 where id= 1;
    这样的 SQL 操作就不是幂等的,一旦重复,结果就会产生变化。
    常见办法
    因此应对消息重复的办法是,使消息接收端的处理是一个幂等操作。这样的做法降低了
    消息中间件的整体复杂性,不过也给使用消息中间件的消息接收端应用带来了一定的限制和
    门槛。

    1. MVCC:
      多版本并发控制,乐观锁的一种实现,在生产者发送消息时进行数据更新时需要带上数
      据的版本号,消费者去更新时需要去比较持有数据的版本号,版本号不一致的操作无法成功。
      例如博客点赞次数自动+1 的接口:
      public boolean addCount(Long id, Long version);
      update blogTable set count= count+1,version=version+1 where id=321 and
      version=123
      每一个 version 只有一次执行成功的机会,一旦失败了生产者必须重新获取数据的最新
      版本号再次发起更新。
    2. 去重表:
      利用数据库表单的特性来实现幂等,常用的一个思路是在表上构建唯一性索引,保证某
      一类数据一旦执行完毕,后续同样的请求不再重复处理了(利用一张日志表来记录已经处理
      成功的消息的 ID,如果新到的消息 ID 已经在日志表中,那么就不再处理这条消息。)
      以电商平台为例子,电商平台上的订单 id 就是最适合的 token。当用户下单时,会经
      历多个环节,比如生成订单,减库存,减优惠券等等。每一个环节执行时都先检测一下该订
      单 id 是否已经执行过这一步骤,对未执行的请求,执行操作并缓存结果,而对已经执行过
      的 id,则直接返回之前的执行结果,不做任何操作。这样可以在最大程度上避免操作的重
      复执行问题,缓存起来的执行结果也能用于事务的控制等。

    26. 消息的可靠性传输

    RabbitMQ
    (1)生产者弄丢了数据
    生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络啥的问
    题,都有可能。此时可以选择用 RabbitMQ 提供的事务功能,就是生产者发送数据之前开启
    RabbitMQ 事务(channel.txSelect),然后发送消息,如果消息没有成功被 RabbitMQ 接收
    到,那么生产者会收到异常报错,此时就可以回滚事务(channel.txRollback),然后重试
    发送消息;如果收到了消息,那么可以提交事务(channel.txCommit)。但是问题是,RabbitMQ
    事务机制一搞,基本上吞吐量会下来,因为太耗性能。
    所以一般来说,如果要确保 RabbitMQ 的消息别丢,可以开启 confirm 模式,在生产者
    那里设置开启 confirm 模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入
    了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ
    没能处理这个消息,会回调你一个 nack 接口,告诉你这个消息接收失败,你可以重试。而
    且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收
    到这个消息的回调,那么你可以重发。
    事务机制和 cnofirm 机制最大的不同在于,事务机制是同步的,你提交一个事务之后会
    阻塞在那儿,但是 confirm 机制是异步的,你发送个消息之后就可以发送下一个消息,然后
    那个消息 RabbitMQ 接收了之后会异步回调你一个接口通知你这个消息接收到了。
    所以一般在生产者这块避免数据丢失,都是用 confirm 机制的。
    (2)RabbitMQ 弄丢了数据
    就是 RabbitMQ 自己弄丢了数据,这个你必须开启 RabbitMQ 的持久化,就是消息写入之
    后会持久化到磁盘,哪怕是 RabbitMQ 自己挂了,恢复之后会自动读取之前存储的数据,一
    般数据不会丢。除非极其罕见的是,RabbitMQ 还没持久化,自己就挂了,可能导致少量数
    据会丢失的,但是这个概率较小。
    设置持久化有两个步骤,第一个是创建 queue 和交换器的时候将其设置为持久化的,这
    样就可以保证 RabbitMQ 持久化相关的元数据,但是不会持久化 queue 里的数据;第二个是
    发送消息的时候将消息的 deliveryMode 设置为 2,就是将消息设置为持久化的,此时
    RabbitMQ 就会将消息持久化到磁盘上去。必须要同时设置这两个持久化才行,RabbitMQ 哪
    怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。
    而且持久化可以跟生产者那边的 confirm 机制配合起来,只有消息被持久化到磁盘之
    后,才会通知生产者 ack 了,所以哪怕是在持久化到磁盘之前,RabbitMQ 挂了,数据丢了,
    生产者收不到 ack,你也是可以自己重发的。
    哪怕是你给 RabbitMQ 开启了持久化机制,也有一种可能,就是这个消息写到了 RabbitMQ
    中,但是还没来得及持久化到磁盘上,结果不巧,此时 RabbitMQ 挂了,就会导致内存里的
    一点点数据会丢失。
    (3)消费端弄丢了数据
    RabbitMQ 如果丢失了数据,主要是因为你消费的时候,刚消费到,还没处理,结果进
    程挂了,比如重启了,那么就尴尬了,RabbitMQ 认为你都消费了,这数据就丢了。
    这个时候得用 RabbitMQ 提供的 ack 机制,简单来说,就是你关闭 RabbitMQ 自动 ack,
    可以通过一个 api 来调用就行,然后每次你自己代码里确保处理完的时候,再程序里 ack
    一把。这样的话,如果你还没处理完,不就没有 ack?那 RabbitMQ 就认为你还没处理完,
    这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。
    Kafka
    (1)消费端弄丢了数据
    唯一可能导致消费者弄丢数据的情况,就是说,你那个消费到了这个消息,然后消费者
    那边自动提交了 offset,让 kafka 以为你已经消费好了这个消息,其实你刚准备处理这个
    消息,你还没处理,你自己就挂了,此时这条消息就丢咯。
    大家都知道 kafka 会自动提交 offset,那么只要关闭自动提交 offset,在处理完之后
    自己手动提交 offset,就可以保证数据不会丢。但是此时确实还是会重复消费,比如你刚
    处理完,还没提交 offset,结果自己挂了,此时肯定会重复消费一次,自己保证幂等性就
    好了。
    生产环境碰到的一个问题,就是说我们的 kafka 消费者消费到了数据之后是写到一个内
    存的 queue 里先缓冲一下,结果有的时候,你刚把消息写入内存 queue,然后消费者会自动
    提交 offset。
    然后此时我们重启了系统,就会导致内存 queue 里还没来得及处理的数据就丢失了
    (2)kafka 弄丢了数据
    这块比较常见的一个场景,就是 kafka 某个 broker 宕机,然后重新选举 partiton 的
    leader 时。大家想想,要是此时其他的 follower 刚好还有些数据没有同步,结果此时 leader
    挂了,然后选举某个 follower 成 leader 之后,他不就少了一些数据?这就丢了一些数据啊。
    所以此时一般是要求起码设置如下 4 个参数:
    给这个 topic 设置 replication.factor 参数:这个值必须大于 1,要求每个 partition
    必须有至少 2 个副本。
    在 kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1,这个是要求一个
    leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader
    挂了还有一个 follower 吧。
    在 producer 端设置 acks=all:这个是要求每条数据,必须是写入所有 replica 之后,
    才能认为是写成功了。
    在 producer 端设置 retries=MAX(很大很大很大的一个值,无限次重试的意思):这
    个是要求一旦写入失败,就无限重试,卡在这里了。
    (3)生产者会不会弄丢数据
    如果按照上述的思路设置了 ack=all,一定不会丢,要求是,你的 leader 接收到消息,
    所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产
    者会自动不断的重试,重试无限次。

    27. 消息的顺序性

    从根本上说,异步消息是不应该有顺序依赖的。在 MQ 上估计是没法解决。要实现严格
    的顺序消息,简单且可行的办法就是:保证生产者 - MQServer - 消费者是一对一对一的关
    系。
    RabbitMQ
    如果有顺序依赖的消息,要保证消息有一个 hashKey,类似于数据库表分区的的分区 key
    列。保证对同一个 key 的消息发送到相同的队列。A 用户产生的消息(包括创建消息和删除
    消息)都按 A 的 hashKey 分发到同一个队列。只需要把强相关的两条消息基于相同的路由就
    行了,也就是说经过 m1 和 m2 的在路由表里的路由是一样的,那自然 m1 会优先于 m2 去投递。
    而且一个 queue 只对应一个 consumer。
    Kafka
    一个 topic,一个 partition,一个 consumer,内部单线程消费

    展开全文
    weixin_42208959 2021-03-28 23:11:45
  • 前言文章开始前,我们先了解一下...是关注于数据的发送和接收,利用高效可靠的异步消息传递机制集成分布式系统图示:消息中间件RabbitMQ+ActiveMQ+Kafka的对比接下来就是消息中间件面试题RabbitMQ+ActiveMQ+KafkaRab...

    前言

    文章开始前,我们先了解一下什么是消息中间件?

    什么是中间件?

    非底层操作系统软件,非业务应用软件,不是直接给最终用户使用的,不能直接给客户带来价值的软件统称为中间件。

    什么是消息中间件?

    是关注于数据的发送和接收,利用高效可靠的异步消息传递机制集成分布式系统

    图示:

    379b83d76aca353e800a3c40acbe1e90.png

    消息中间件RabbitMQ+ActiveMQ+Kafka的对比

    3f5749d0cbcb6a350ba0b1a9260f7899.png

    接下来就是消息中间件面试题RabbitMQ+ActiveMQ+Kafka

    RabbitMQ消息中间件系列

    1:RabbitMQ 中的 broker 是指什么?cluster 又是指什么?

    答:broker 是指一个或多个 erlang node 的逻辑分组,且 node 上运行着 RabbitMQ 应用程序。cluster 是在 broker 的基础之上,增加了 node 之间共享元数据的约束。

    2:什么是元数据?元数据分为哪些类型?包括哪些内容?与 cluster 相关的元数据有哪些?元数据是如何保存的?元数据在 cluster 中是如何分布的?

    答:在非 cluster 模式下,元数据主要分为 Queue 元数据(queue 名字和属性等)、Exchange 元数据(exchange 名字、类型和属性等)、Binding 元数据(存放路由关系的查找表)、Vhost 元数据(vhost 范围内针对前三者的名字空间约束和安全属性设置)。在cluster 模式下,还包括 cluster 中 node 位置信息和 node 关系信息。元数据按照 erlang node 的类型确定是仅保存于 RAM 中,还是同时保存在 RAM 和 disk 上。元数据在cluster 中是全 node 分布的。

    3:RAM node 和 disk node 的区别?

    答:RAM node 仅将 fabric(即 queue、exchange 和 binding 等 RabbitMQ 基础构件)相关元数据保存到内存中,但 disk node 会在内存和磁盘中均进行存储。RAM node 上唯一会存储到磁盘上的元数据是 cluster 中使用的 disk node 的地址。要求在 RabbitMQ cluster 中至少存在一个 disk node 。

    4:RabbitMQ 上的一个 queue 中存放的 message 是否有数量限制?

    答:可以认为是无限制,因为限制取决于机器的内存,但是消息过多会导致处理效率的下降。

    5:RabbitMQ 概念里的 channel、exchange 和 queue 这些东东是逻辑概念,还是对应着进程实体?这些东东分别起什么作用?

    答:queue 具有自己的 erlang 进程;exchange 内部实现为保存 binding 关系的查找表; channel 是实际进行路由工作的实体,即负责按照 routing_key 将 message 投递给queue 。由 AMQP 协议描述可知,channel 是真实 TCP 连接之上的虚拟连接,所有AMQP 命令都是通过 channel 发送的,且每一个 channel 有唯一的 ID。一个 channel 只能被单独一个操作系统线程使用,故投递到特定 channel 上的 message 是有顺序的。但一个操作系统线程上允许使用多个 channel 。channel 号为 0 的 channel 用于处理所有对于当前 connection 全局有效的帧,而 1-65535 号 channel 用于处理和特定 channel 相关的帧。

    其中每一个 channel 运行在一个独立的线程上,多线程共享同一个 socket。

    6:vhost 是什么?起什么作用?

    答:vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的

    queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。

    7:为什么 heavy RPC 的使用场景下不建议采用 disk node ?

    答:heavy RPC 是指在业务逻辑中高频调用 RabbitMQ 提供的 RPC 机制,导致不断创建、销毁 reply queue ,进而造成 disk node 的性能问题(因为会针对元数据不断写盘)。所以在使用 RPC 机制时需要考虑自身的业务场景。

    8:向不存在的 exchange 发 publish 消息会发生什么?向不存在的 queue 执行consume 动作会发生什么?

    答:都会收到 Channel.Close 信令告之不存在(内含原因 404 NOT_FOUND)。

    9:RabbitMQ 允许发送的 message 最大可达多大?

    答:根据 AMQP 协议规定,消息体的大小由 64-bit 的值来指定,所以你就可以知道到底能发多大的数据了。

    10:什么情况下 producer 不主动创建 queue 是安全的?

    答:1.message是允许丢失的;2.实现了针对未处理消息的republish功能(例如采用Publisher Confirm 机制)。

    11:“dead letter”queue 的用途?

    答:当消息被 RabbitMQ server 投递到 consumer 后,但 consumer 却通过 Basic.Reject 进行了拒绝时(同时设置 requeue=false),那么该消息会被放入“dead letter”queue 中。该 queue 可用于排查 message 被 reject 或 undeliver 的原因。

    12:为什么说保证 message 被可靠持久化的条件是 queue 和 exchange 具有durable 属性,同时 message 具有 persistent 属性才行?

    答:binding 关系可以表示为 exchange – binding – queue 。从文档中我们知道,若要求投递的 message 能够不丢失,要求 message 本身设置 persistent 属性,要求 exchange 和 queue 都设置 durable 属性。其实这问题可以这么想,若 exchange 或 queue 未设置durable 属性,则在其 crash 之后就会无法恢复,那么即使 message 设置了 persistent 属性,仍然存在 message 虽然能恢复但却无处容身的问题;同理,若 message 本身未设置persistent 属性,则 message 的持久化更无从谈起。

    13:什么情况下会出现 blackholed 问题?

    答:blackholed 问题是指,向 exchange 投递了 message ,而由于各种原因导致该message 丢失,但发送者却不知道。可导致 blackholed 的情况:1.向未绑定 queue 的exchange 发送 message;2.exchange 以 binding_key key_A 绑定了 queue queue_A,但向该 exchange 发送 message 使用的 routing_key 却是 key_B。

    14:如何防止出现 blackholed 问题?

    答:没有特别好的办法,只能在具体实践中通过各种方式保证相关 fabric 的存在。另外, 如果在执行 Basic.Publish 时设置 mandatory=true ,则在遇到可能出现 blackholed 情况时,服务器会通过返回 Basic.Return 告之当前 message 无法被正确投递(内含原因 312 NO_ROUTE)。

    15:Consumer Cancellation Notification 机制用于什么场景?

    答:用于保证当镜像 queue 中 master 挂掉时,连接到 slave 上的 consumer 可以收到自身 consume 被取消的通知,进而可以重新执行 consume 动作从新选出的 master 出获得消息。若不采用该机制,连接到 slave 上的 consumer 将不会感知 master 挂掉这个事情,导致后续无法再收到新 master 广播出来的 message 。另外,因为在镜像 queue 模式下,存在将 message 进行 requeue 的可能,所以实现 consumer 的逻辑时需要能够正确处理出现重复 message 的情况。

    9b62309fd602a79f565d35663927b4d1.png

    ActiveMQ消息中间件系列

    1.什么是 ActiveMQ?

    activeMQ 是一种开源的,实现了 JMS1.1 规范的,面向消息(MOM)的中间件,为应用程序提供高效的、可扩展的、稳定的和安全的企业级消息通信

    2.ActiveMQ 服务器宕机怎么办?

    这得从 ActiveMQ 的储存机制说起。在通常的情况下,非持久化消息是存储在内存中的,持久化消息是存储在文件中的,它们的最大限制在配置文件的节点中配置。但是,在非持久化消息堆积到一定程度,内存告急的时候,ActiveMQ 会将内存中的非持久化消息写入临时文件中,以腾出内存。虽然都保存到了文件里,但它和持久化消息的区别是,重启后持久化消息会从文件中恢复,非持久化的临时文件会直接删除。

    那如果文件增大到达了配置中的最大限制的时候会发生什么?我做了以下实验:

    设置 2G 左右的持久化文件限制,大量生产持久化消息直到文件达到最大限制,此时生产者阻塞,但消费者可正常连接并消费消息,等消息消费掉一部分,文件删除又腾出空间之后,生产者又可继续发送消息, 服务自动恢复正常。

    设置 2G 左右的临时文件限制,大量生产非持久化消息并写入临时文件,在达到最大限制时,生产者阻塞,消费者可正常连接但不能消费消息,或者原本慢速消费的消费者,消费突然停止。整个系统可连接, 但是无法提供服务,就这样挂了。

    具体原因不详,解决方案:尽量不要用非持久化消息,非要用的话,将临时文件限制尽可能的调大。

    3.丢消息怎么办?

    这得从 java 的 java.net.SocketException 异常说起。简单点说就是当网络发送方发送一堆数据,然后调用 close 关闭连接之后。这些发送的数据都在接收者的缓存里,接收者如果调用 read 方法仍旧能从缓存中读取这些数据,尽管对方已经关闭了连接。但是当接收者尝试发送数据时,由于此时连接已关闭,所以会发生异常,这个很好理解。不过需要注意的是,当发生 SocketException 后,原本缓存区中数据也作废了,此时接收者再次调用 read 方法去读取缓存中的数据,就会报 Software caused connection abort: recv failed 错误。

    通过抓包得知,ActiveMQ 会每隔 10 秒发送一个心跳包,这个心跳包是服务器发送给客户端的,用来判断客户端死没死。如果你看过上面第一条,就会知道非持久化消息堆积到一定程度会写到文件里,这个写的过程会阻塞所有动作,而且会持续 20 到 30 秒,并且随着内存的增大而增大。当客户端发完消息调用connection.close()时,会期待服务器对于关闭连接的回答,如果超过 15 秒没回答就直接调用 socket 层的 close 关闭 tcp 连接了。这时客户端发出的消息其实还在服务器的缓存里等待处理,不过由于服务器心跳包的设置,导致发生了 java.net.SocketException 异常,把缓存里的数据作废了,没处理的消息全部丢失。

    解决方案:用持久化消息,或者非持久化消息及时处理不要堆积,或者启动事务,启动事务后,

    commit()方法会负责任的等待服务器的返回,也就不会关闭连接导致消息丢失了。

    4.持久化消息非常慢。

    默认的情况下,非持久化的消息是异步发送的,持久化的消息是同步发送的,遇到慢一点的硬盘,发送消息的速度是无法忍受的。但是在开启事务的情况下,消息都是异步发送的,效率会有 2 个数量级的提升。所以在发送持久化消息时,请务必开启事务模式。其实发送非持久化消息时也建议开启事务,因为根本不会影响性能。

    5.消息的不均匀消费。

    有时在发送一些消息之后,开启 2 个消费者去处理消息。会发现一个消费者处理了所有的消息,另一个消费者根本没收到消息。原因在于 ActiveMQ 的 prefetch 机制。当消费者去获取消息时,不会一条一条去获取,而是一次性获取一批,默认是 1000 条。这些预获取的消息,在还没确认消费之前,在管理控制台还是可以看见这些消息的,但是不会再分配给其他消费者,此时这些消息的状态应该算作“已分配未消 费”,如果消息最后被消费,则会在服务器端被删除,如果消费者崩溃,则这些消息会被重新分配给新的消费者。但是如果消费者既不消费确认,又不崩溃,那这些消息就永远躺在消费者的缓存区里无法处理。更通常的情况是,消费这些消息非常耗时,你开了 10 个消费者去处理,结果发现只有一台机器吭哧吭哧

    处理,另外 9 台啥事不干。

    解决方案:将 prefetch 设为 1,每次处理 1 条消息,处理完再去取,这样也慢不了多少。

    6.死信队列。

    如果你想在消息处理失败后,不被服务器删除,还能被其他消费者处理或重试,可以关闭AUTO_ACKNOWLEDGE,将 ack 交由程序自己处理。那如果使用了 AUTO_ACKNOWLEDGE,消息是什么时候被确认的,还有没有阻止消息确认的方法?有!

    消费消息有 2 种方法,一种是调用 consumer.receive()方法,该方法将阻塞直到获得并返回一条消息。这种情况下,消息返回给方法调用者之后就自动被确认了。另一种方法是采用 listener 回调函数,在有消息到达时,会调用 listener 接口的 onMessage 方法。在这种情况下,在 onMessage 方法执行完毕后, 消息才会被确认,此时只要在方法中抛出异常,该消息就不会被确认。那么问题来了,如果一条消息不能被处理,会被退回服务器重新分配,如果只有一个消费者,该消息又会重新被获取,重新抛异常。就算有多个消费者,往往在一个服务器上不能处理的消息,在另外的服务器上依然不能被处理。难道就这么退回

    –获取–报错死循环了吗?

    在重试 6 次后,ActiveMQ 认为这条消息是“有毒”的,将会把消息丢到死信队列里。如果你的消息不见了,去 ActiveMQ.DLQ 里找找,说不定就躺在那里。

    9c21656cee8b8c9730f5e16ae8399cbf.png

    Kafka消息中间件系列

    1.Kafka 的设计时什么样的呢?

    Kafka 将消息以 topic 为单位进行归纳

    将向 Kafka topic 发布消息的程序成为 producers.

    将预订 topics 并消费消息的程序成为 consumer.

    Kafka 以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个 broker. producers 通过网络将消息发送到 Kafka 集群,集群向消费者提供消息

    2.数据传输的事物定义有哪三种?

    数据传输的事务定义通常有以下三种级别:

    (1)最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输

    (2)最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.

    (3)精确的一次(Exactly once):不会漏传输也不会重复传输,每个消息都传输被一次而且仅仅被传输一次,这是大家所期望的

    3.Kafka 判断一个节点是否还活着有那两个条件?

    (1)节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接

    (2)如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久

    4.producer 是否直接将数据发送到 broker 的 leader(主节点)?

    producer 直接将数据发送到 broker 的 leader(主节点),不需要在多个节点进行分发,为了帮助 producer 做到这点,所有的 Kafka 节点都可以及时的告知:哪些节点是活动的,目标topic 目标分区的 leader 在哪。这样 producer 就可以直接将消息发送到目的地了

    5、Kafa consumer 是否可以消费指定分区消息?

    Kafaconsumer 消费消息时,向 broker 发出"fetch"请求去消费特定分区的消息,consumer 指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer 拥有了 offset 的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的

    6、Kafka 消息是采用 Pull 模式,还是 Push 模式?

    Kafka 最初考虑的问题是,customer 应该从 brokes 拉取消息还是 brokers 将消息推送到consumer,也就是 pull 还 push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消息推送到 broker,consumer 从 broker 拉取消息

    一些消息系统比如 Scribe 和 ApacheFlume 采用了 push 模式,将消息推送到下游的consumer。这样做有好处也有坏处:由 broker 决定消息推送的速率,对于不同消费速率的consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息,但不幸的是,push 模式下,当 broker 推送的速率远大于 consumer 消费的速率时, consumer 恐怕就要崩溃了。最终 Kafka 还是选取了传统的 pull 模式

    Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push 模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull 模式下,consumer 就可以根据自己的消费能力去决

    定这些策略

    Pull 有个缺点是,如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询, 直到新消息到 t 达。为了避免这点,Kafka 有个参数可以让 consumer 阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发

    7.Kafka 的消费者如何消费数据

    消费者每次消费数据的时候,消费者都会记录消费的物理偏移量(offset)的位置等到下次消费时,他会接着上次位置继续消费

    8.消费者负载均衡策略

    一个消费者组中的一个分片对应一个消费者成员,他能保证每个消费者成员都能访问,如果组中成员太多会有空闲的成员

    9.数据有序

    一个消费者组里它的内部是有序的消费者组与消费者组之间是无序的

    10.kafaka 生产数据时数据的分组策略

    生产者决定数据产生到集群的哪个 partition 中每一条消息都是以(key,value)格式

    Key 是由生产者发送数据传入

    所以生产者(key)决定了数据产生到集群的哪个 partition

    展开全文
    weixin_35783593 2021-03-08 06:27:01
  • 消息中间件消息中间件是什么消息中间件的两种传递模式消息中间件的作用常见的消息中间件 消息中间件是什么 是利用高效可靠的消息传递机制进行异步的数据传输,并基于数据通信进行分布式系统的集成。通过提供消息...

    (原创不易,你们对阿超的赞就是阿超持续更新的动力!)

    (以免丢失,建议收藏,阿超持续更新中…)

    (------------------------------------------------------------------------)

    消息中间件是什么

    是利用高效可靠的消息传递机制进行异步的数据传输,并基于数据通信进行分布式系统的集成。通过提供消息队列模型和消息传递机制,可以在分布式环境下扩展进程间的通信

    消息中间件的两种传递模式

    • 点对点模式
    • 发布/订阅模式

    消息中间件的作用

    • 异步处理
    • 应用解耦
    • 冗余
    • 流量削峰
      • 可以控制活动人数
      • 可以缓解短时间内高流量压垮应用
    • 消息通讯
    • 缓冲
    • 可恢复性

    常见的消息中间件

    这里写图片描述

    ---------------------------面试题总结-----------------------------

    Java基础知识点总结

    MyBatis常见面试题总结

    Spring常见面试题总结

    SpringMVC常见面试题总结

    SpringBoot常见面试题总结

    消息中间件常见面试题总结

    Kafka常见面试题

    Redis面试题总结

    SQL常见面试题总结

    Dubbo常见面试题总结

    SpringCloud常见面试题总结

    TCP和UDP详解

    数据库事务详解

    展开全文
    Mr_Gaojinchao 2021-11-06 13:52:20
  • 面试官问这个问题的期望之一的回答是,你们公司有什么业务场景,这个业务场景有什么技术挑战,如果不用MQ可能会很麻烦,但是再用了之后带来了很多好处。 消息队列的常见使用场景有很多但是核心的有三个:解耦、异步...

    对原文章部分知识点做了补充

    1. 为什么使用消息队列

    • 面试官问这个问题的期望之一的回答是,你们公司有什么业务场景,这个业务场景有什么技术挑战,如果不用MQ可能会很麻烦,但是再用了之后带来了很多好处。
    • 消息队列的常见使用场景有很多但是核心的有三个:解耦、异步、削峰

    1.1 解耦

    • 场景描述:A系统发送个数据到BCD三个系统,接口调用发送,那如果E系统也要这个数据呢?那如果C系统现在不需要了呢?现在A系统又要发送第二种数据了呢?A系统负责人崩溃中…再来点崩溃的事儿,A系统要时时刻刻考虑BCDE四个系统如果挂了怎么办?那我要不要重发?我要不要把消息存起来?头发都白了啊…
      使用mq场景
    • 使用了MQ之后的解耦场景
      使用mq后的场景
    • 面试技巧:你需要考虑下,你负责的系统中是否有类似的场景,就是一个系统或者一个模块,调用了多个系统或者模块,相互之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果MQ给他异步化解耦也是可以的,你就需要去考虑在你的项目里是不是可以运用这个MQ去进行系统解耦 。

    1.2 异步

    • 场景描述:系统A接受一个请求,需要在自己本地写库,还需要在系统BCD三个系统写库,自己本地写库需要3ms。BCD分别需要300ms、450ms、200ms。最终总好时长:953ms,接近1s。给用户的体验感觉一点也不好。
    • 不用MQ的同步高延时请求场景
      不用mq
    • 使用MQ异步化之后的接口性能优化
      使用mq

    1.3 削峰

    • 场景描述:每天 0 点到 11 点,系统A风平浪静,每秒并发请求数量就 100 个。结果每一一到11点到1点,每秒并发请求数量就会暴增大1万条 。但是系统最大的处理能力就只能每秒钟处理1000个请求。
    • 没有用MQ的时候高峰期系统被打死的场景
      没用mq
    • 使用MQ来进行削峰的场景
      使用mq

    2. 消息队列的优缺点

    2.1 优点

    • 特殊场景下解耦、异步、削峰。

    2.2 缺点

    • 系统可用性降低:系统引入的外部依赖越多,越容易挂掉,本来你就是A系统调用BCD三个系统的接口就好了,人ABCD四个系统好好的没什么问题,你偏加个MQ进来,万一MQ挂了怎么办,整套系统崩溃了,就完蛋了
    • 系统复杂性提高:硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?
    • 一致性问题:系统A处理完了直接返回成功了,人家都认为你这个请求成功了;但问题是,要是BCD三个系统哪里BD系统成功了,结果C系统写库失败了,咋整?数据就不一致了,
      使用mq的缺点
    • 所以消息队列是一种非常复杂的架构,引入它有很多好处,但是也得针对他带来的坏处做各种额外的技术方案和架构来规避掉。做好之后你会发现系统复杂度提升了一个数量积,但是关键时刻,用,还是要用的。

    3. kafka、rabbitmq、rocketmq的优缺点

    特性ActiveMQRabbitMQRocketMQKafka
    单机吞吐量万级,吞吐量比RocketMQ和Kafka要低了一个数量级万级,吞吐量比RocketMQ和Kafka要低了一个数量级10万级,RocketMQ也是可以支撑高吞吐的一种MQ10万级别,这是kafka最大的优点,就是吞吐量高。一般配合大数据类的系统来进行实时数据计算、日志采集等场景
    topic数量对吞吐量的影响--topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降。这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topictopic从几十个到几百个的时候,吞吐量会大幅度下降。所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源
    时效性ms级微秒级,这是rabbitmq的一大特点,延迟是最低的ms级延迟在ms级以内
    可用性高,基于主从架构实现高可用性高,基于主从架构实现高可用性非常高,分布式架构非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
    消息可靠性有较低的概率丢失数据经过参数优化配置,可以做到0丢失经过参数优化配置,可以做到0丢失经过参数优化配置,可以做到0丢失
    特点MQ领域的功能极其完备基于erlang开发,所以并发能力很强,性能极其好,延时很低MQ功能较为完善,还是分布式的,扩展性好功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准
    优点非常成熟,功能强大,在业内大量的公司以及项目中都有应用erlang语言开发,性能极其好,延时很低;吞吐量到万级,MQ功能比较完备;而且开源提供的管理界面非常棒,用起来很好用;社区相对比较活跃,版本更新快;使用公司多接口简单易用;大规模吞吐,性能也非常好,分布式扩展也很方便,社区维护还可以,可靠性和可用性高,还可以支撑大规模的topic数量,支持复杂MQ业务场景;源码是Java方便定制。提供超高的吞吐量,ms级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展;同时kafka最好是支撑较少的topic数量即可,保证其超高吞吐量
    缺点偶尔会有较低概率丢失消息。社区不活跃,版本更新慢。基于解耦和异步来用的,较少在大规模吞吐的场景中使用吞吐量较低;rabbitmq集群动态扩展较复杂;erlang开发人员少,不易于定制社区活跃度一般有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略

    4. 引入消息队列之后如何保证其高可用性

    4.1 RabbitMQ的高可用性

    • RabbitMQ是比较有代表性的,因为是基于主从做高可用性的,我们就以他为例子讲解第一种MQ的高可用性怎么实现。
    • Rabbitmq有三种模式:单机模式,普通集群模式,镜像集群模式

    单机模式

    • demo级别,一般就是你本地启动了玩玩儿的,没人生产用单机模式

    普通集群模式

    • 在多台机器上启动多个rabbitmq实例,每个机器启动一个。但是你创建的queue,只会放在一个rabbtimq实例上,但是每个实例都同步queue的元数据。完了你消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从queue所在实例上拉取数据过来。
    • 非分布式,就是个普通集群。因为这导致你要么消费者每次随机连接一个实例然后拉取数据,要么固定连接那个queue所在实例消费数据,前者有数据拉取的开销,后者导致单实例性能瓶颈。
    • 如果那个放queue的实例宕机了,会导致接下来其他实例就无法从那个实例拉取,如果你开启了消息持久化,让rabbitmq落地存储消息的话,消息不一定会丢,得等这个实例恢复了,然后才可以继续从这个queue拉取数据。
    • 主要是提高吞吐量的,就是说让集群中多个节点来服务某个queue的读写操作。
      普通集群模式

    镜像集群模式

    • 是高可用模式,跟普通集群模式不一样的是,你创建的queue,无论元数据还是queue里的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会自动把消息到多个实例的queue里进行消息同步。
    • 优点:任何一个机器宕机了,没事儿,别的机器都可以用。
    • 缺点:
      • 第一:性能开销大,消息同步所有机器,导致网络带宽压力和消耗很重!
      • 第二,扩展性低,如果某个queue负载很重,你加机器,新增的机器也包含了这个queue的所有数据,并没有办法线性扩展你的queue
    • 可以通过管理控制台开启这个镜像集群模式,在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候可以要求数据同步到所有节点的,也可以要求就同步到指定数量的节点,然后你再次创建queue的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。

    镜像集群模式

    4.2 Kafka的高可用性

    • kafka一个最基本的架构认识:多个broker组成,每个broker是一个节点;你创建一个topic,这个topic可以划分为多个partition,每个partition可以存在于不同的broker上,每个partition就放一部分数据。
    • 这就是天然的分布式消息队列,就是说一个topic的数据,是分散放在多个机器上的,每个机器就放一部分数据。
    • 实际上rabbitmq之类的,并不是分布式消息队列,他就是传统的消息队列,只不过提供了一些集群、HA的机制而已,因为无论怎么玩儿,rabbitmq一个queue的数据都是放在一个节点里的,镜像集群下,也是每个节点都放这个queue的完整数据。
    • kafka 0.8以前,是没有HA机制的,就是任何一个broker宕机了,那个broker上的partition就废了,没法写也没法读,没有什么高可用性可言。
    • kafka 0.8以后,提供了HA机制,就是replica副本机制。每个partition的数据都会同步到吉他机器上,形成自己的多个replica副本。然后所有replica会选举一个leader出来,那么生产和消费都跟这个leader打交道,然后其他replica就是follower。写的时候,leader会负责把数据同步到所有follower上去,读的时候就直接读leader上数据即可。只能读写leader?很简单,要是你可以随意读写每个follower,那么就要care数据一致性的问题,系统复杂度太高,很容易出问题。kafka会均匀的将一个partition的所有replica分布在不同的机器上,这样才可以提高容错性。
    • 这么搞,就有所谓的高可用性了,因为如果某个broker宕机了,没事儿,那个broker上面的partition在其他机器上都有副本的,如果这上面有某个partition的leader,那么此时会重新选举一个新的leader出来,大家继续读写那个新的leader即可。这就有所谓的高可用性了。
    • 写数据的时候,生产者就写leader,然后leader将数据落地写本地磁盘,接着其他follower自己主动从leader来pull数据。一旦所有follower同步好数据了,就会发送ack给leader,leader收到所有follower的ack之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)
    • 消费的时候,只会从leader去读,但是只有一个消息已经被所有follower都同步成功返回ack的时候,这个消息才会被消费者读到。
    • 实际上这块机制,讲深了,是可以非常之深入的,但是我还是回到我们这个课程的主题和定位,聚焦面试,至少你听到这里大致明白了kafka是如何保证高可用机制的了,对吧?不至于一无所知,现场还能给面试官画画图。要遇上面试官确实是kafka高手,深挖了问,那你只能说不好意思,太深入的你没研究过。
    • 但是大家一定要明白,这个事情是要权衡的,你现在是要快速突击常见面试题体系,而不是要深入学习kafka,要深入学习kafka,你是没那么多时间的。你只能确保,你之前也许压根儿不知道这块,但是现在你知道了,面试被问到,你大概可以说一说。然后很多其他的候选人,也许还不如你,没看过这个,被问到了压根儿答不出来,相比之下,你还能说点出来,大概就是这个意思了。
      kafka的高可用

    5. 如何保证消息消费时的幂等性

    • 幂等性:一个请求重复请求多次,需要确保对应的数据是不会改变的,多次请求结果相同。
    • 如何保证消费不被重复消费
      消息重复消费
    • 如何保证消息的幂等性
      保证幂等性

    6. 如何保证消息的可靠传输

    • 用mq有个基本原则,就是数据不能多一条,也不能少一条,不能多,就是刚才说的重复消费和幂等性问题。不能少,就是说这数据别搞丢了。那这个问题你必须得考虑一下。

    6.1 Rabbitmq

    6.1.1 生产者丢数据

    • 生产者将数据发送到rabbitmq的时候,可能数据就在半路给搞丢了,因为网络啥的问题,都有可能。

    解决方案

    • 1.事务机制(不推荐,异步方式)
      对于 RabbitMQ 来说,生产者发送数据之前开启 RabbitMQ 的事务机制channel.txselect ,如果消息没有进队列,则生产者受到异常报错,并进行回滚 channel.txRollback,然后重试发送消息;如果收到了消息,则可以提交事务 channel.txCommit。但这是一个同步的操作,会影响性能。
    • 2.confirm 机制(推荐,异步方式)
      每次生产者发送的消息都会分配一个唯一的 id,如果写入到了 RabbitMQ 队列中,则 RabbitMQ 会回传一个 ack 消息,说明这个消息接收成功。如果 RabbitMQ 没能处理这个消息,则回调 nack 接口。说明需要重试发送消息。
    • 事务模式 和 confirm 模式的区别:
      • 事务机制是同步的,提交事务后悔被阻塞直到提交事务完成后。
      • confirm 模式异步接收通知,但可能接收不到通知。需要考虑接收不到通知的场景。

    6.1.2 rabbitmq自己丢数据

    • 解决方案:创建 Queue 的时候将其设置为持久化。
    • 设置持久化有两个步骤
      • 第一:创建queue的时候将其设置为持久化的,这样就可以保证rabbitmq持久化queue的元数据,但是不会持久化queue里的数据;
      • 第二:发送消息的时候将消息的deliveryMode设置为2,就是将消息设置为持久化的,此时rabbitmq就会将消息持久化到磁盘上去。必须要同时设置这两个持久化才行,rabbitmq哪怕是挂了,再次重启,也会从磁盘上重启恢复queue,恢复这个queue里的数据。
      • 持久化可以跟生产者那边的confirm机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者ack了,所以哪怕是在持久化到磁盘之前,rabbitmq挂了,数据丢了,生产者收不到ack,你也是可以自己重发的。
    • 哪怕是你给rabbitmq开启了持久化机制,也有一种可能,就是这个消息写到了rabbitmq中,但是还没来得及持久化到磁盘上,结果不巧,此时rabbitmq挂了,就会导致内存里的一点点数据会丢失。

    6.1.3 消费者丢数据

    • rabbitmq如果丢失了数据,主要是因为你消费的时候,刚消费到,还没处理,结果进程挂了,比如重启了,那么就尴尬了,rabbitmq认为你都消费了,这数据就丢了。
    • 这个时候得用rabbitmq提供的ack机制,简单来说,就是你关闭rabbitmq自动ack,可以通过一个api来调用就行,然后每次你自己代码里确保处理完的时候,再程序里ack一把。这样的话,如果你还没处理完,不就没有ack?那rabbitmq就认为你还没处理完,这个时候rabbitmq会把这个消费分配给别的consumer去处理,消息是不会丢的。
      消费者丢数据

    6.2 kafka

    6.2.1 生产者会不会弄丢数据

    • 如果producer端设置acks=all,一定不会丢,即要求leader接收到消息,所有的follower都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。

    6.2.2 kafka自己弄丢数据

    • 常见的一个场景,就是kafka某个broker宕机,然后重新选举partiton的leader时。

    • 要是此时其他的follower刚好还有些数据没有同步,结果此时leader挂了,然后选举某个follower成leader之后,他不就少了一些数据?这就丢了一些数据啊。
      kafka自己丢了数据

    • 保证在leader所在broker发生故障,进行leader切换时,数据不会丢失,一般是要求设置如下4个参数:

      • topic设置replication.factor参数:这个值必须大于1,要求每个partition必须有至少2个副本
      • kafka服务端设置min.insync.replicas参数:这个值必须大于1,这个是要求一个leader至少感知到有至少一个follower还跟自己保持联系,没掉队,这样才能确保leader挂了还有一个follower吧
      • producer端设置acks=all:这个是要求每条数据,必须是写入所有replica之后,才能认为是写成功了
      • producer端设置retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里了

    6.2.3 消费者弄丢数据

    • 唯一可能导致消费者弄丢数据的情况,就是说,你那个消费到了这个消息,然后消费者那边自动提交了offset,让kafka以为你已经消费好了这个消息,其实你刚准备处理这个消息,你还没处理,你自己就挂了,此时这条消息就丢咯。
    • 这不是一样么,大家都知道kafka会自动提交offset,那么只要关闭自动提交offset,在处理完之后自己手动提交offset,就可以保证数据不会丢。但是此时确实还是会重复消费,比如你刚处理完,还没提交offset,结果自己挂了,此时肯定会重复消费一次,自己保证幂等性就好了。
    • 生产环境碰到的一个问题,就是说我们的kafka消费者消费到了数据之后是写到一个内存的queue里先缓冲一下,结果有的时候,你刚把消息写入内存queue,然后消费者会自动提交offset。
    • 然后此时我们重启了系统,就会导致内存queue里还没来得及处理的数据就丢失了

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

    7.1 Rabbitmq

    • 数据错乱的场景:一个queue,多个consumer,这不明显乱了
      rabbitmq消息错乱的场景
    • 保证消息顺序性的方案:拆分多个queue,每个queue一个consumer,就是多一些queue而已,或者一个queue但是对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理
      rabbitmq保证消息顺序性

    7.2 kafka

    • 数据错乱的场景:一个topic,一个partition,一个consumer,内部多线程,这不也明显乱了
      kafka消息错乱

    • 保证消息顺序性的方案:一个topic,一个partition,一个consumer,内部单线程消费,写N个内存queue,然后N个线程分别消费一个内存queue即可
      kafka保证消息顺序性

    8. 消息队列积压或满了该怎么处理

    8.1 kafka消费积压

    • 解决方案:临时紧急扩容,具体操作步骤和思路如下:
      1)先修复consumer的问题,确保其恢复消费速度,然后将现有cnosumer都停掉
      2)新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量
      3)然后写一个临时的分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue
      4)接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据
      5)这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据
      6)等快速消费完积压数据之后,得恢复原先部署架构,重新用原先的consumer机器来消费消息
      kafka消息积压处理流程图

    8.2 Rabbitmq消息积压

    解决方案

    • 修复代码层面消费者的问题,确保后续消费速度恢复或尽可能加快消费的速度。
    • 停掉现有的消费者。
    • 临时建立好原先 5 倍的 Queue 数量。
    • 临时建立好原先 5 倍数量的 消费者。
    • 将堆积的消息全部转入临时的 Queue,消费者来消费这些 Queue。
      消息积压处理流程图

    8.3 队列写满

    解决方案:

    • 判断哪些是无用的消息,RabbitMQ 可以进行 Purge Message 操作。
    • 如果是有用的消息,则需要将消息快速消费,将消息里面的内容转存到数据库。
    • 准备好程序将转存在数据库中的消息再次重导到消息队列。
    • 闲时重导消息到消息队列。

    9. 如何设计一个消息队列

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

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

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

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

    • 其实一个mq肯定是很复杂的,面试官问你这个问题,其实是个开放题,他就是看看你有没有从架构角度整体构思和设计的思维以及能力。确实这个问题可以刷掉一大批人,因为大部分人平时不思考这些东西。
    展开全文
    baiye_xing 2021-02-16 21:41:06
  • qq_62402100 2021-10-28 19:33:19
  • weixin_42446705 2021-03-13 13:16:38
  • weixin_47587864 2020-12-21 16:43:36
  • a745233700 2021-03-22 02:46:39
  • afreon 2021-11-05 13:30:13
  • Feng_wwf 2021-07-06 23:54:13
  • miaoao611 2021-10-22 23:28:34
  • u012889902 2021-12-17 13:51:47
  • weixin_44219219 2021-01-24 00:50:20
  • qq_33207292 2021-06-03 19:27:58
  • u010972055 2021-06-06 17:34:52
  • xinshuzhan 2021-04-11 15:41:55
  • qq_26946745 2021-08-22 13:11:33
  • m0_57205780 2021-06-24 14:22:10
  • weixin_31190099 2021-03-13 13:16:52
  • weixin_39667626 2021-03-04 00:16:55
  • weixin_45188187 2021-06-11 17:57:00
  • colspanprince 2021-02-21 10:53:36
  • wxyasj 2021-05-06 08:21:55
  • python113 2021-02-08 14:42:04
  • m0_56662340 2021-05-17 10:40:12
  • lsx2017 2021-02-23 22:39:43
  • wtyicy 2021-04-21 11:22:08
  • b_ingrem 2021-03-08 16:24:07

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 29,387
精华内容 11,754
关键字:

消息中间件面试题