Kafka高频面试题(消息中间件)

一、什么是消息中间件
    可以看成是在消息的传输过程中保存消息的容器(只保存但不做修改,把他看做日志级别的)。
    消息中间件在将消息从它的源中继到他的目标时充当中间人的作用。通过提供消息队列模型和消息传递机制,可以在分布式环境下进行扩展进程间的通信。开发人员不需要考虑网络协议和远程调用的问题,只需要通过各消息中间件所提供的api,就可以简单的完成消息推送和消息接收的业务功能。
 

二、组成

  1. Broker
    消息服务器,为server提供消息核心服务

  2. Producer
    消息生产者,业务的发起方,负责生产消息传输给broker

  3. Consumer
    消息消费者,业务的处理方,负责从broker获取消息并进行业务逻辑处理

  4. Topic
    主题,发布订阅模式下的消息统一汇集地,不同生产者向topic发送消息,由MQ服务器分发到不同的订阅者,实现消息的广播

  5. Queue
    队列,PTP模式下,特定生产者向特定queue发送消息,消费者订阅特定的queue完成指定消息的接收

  6. Message
    消息体,根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输
     

三、传递模式

1、点对点模式
    消息生产者将消息发送到消息队列,消息消费者从这个消息队列中取出消息。消息在传给接收者之前存储在消息队列中,队列消息 可放在内存也可持久化,以保证在消息服务出现故障时消息能够正常传递。
在这里插入图片描述
特点:

  • 每个消息只有一个接收者;
  • 发送者和接收者之间没有时间依赖关系;

2、发布/订阅模式(Pub/Sub)
    通过一个内容节点进行发布和订阅消息,该节点称为主题(topic)。消息的发布者将消息发布到某个topic,消息的订阅者(0个或者多个)订阅这个主题的消息,topic作用相当于中介,它将消息的发布和订阅相互独立开来。
在这里插入图片描述
特点:

  • 每个消息可有多个订阅者;
  • 订阅者只有订阅后才能接收到消息;
  • 包括持久订阅和非持久订阅。

注意:

  1. 发布者和订阅者之间有时间依赖:即订阅者需要与发布者建立订阅关系才能收到消息;
  2. 持久订阅:订阅关系建立后,消息不会消失,不管订阅者是否在线;
  3. 非持久订阅:订阅者为了接收消息需要一直在线,当只有一个订阅者时类似于点对点模式。

 
四、两种消费模式pull和push

1、pull模式
    生产者(Producer)发出的消息到达后,消息服务器(broker)什么也不做,等待消费者(consumer)主动到broker来拉取消息。即消费者需要主动调用http数据接口,然后处理业务数据,处理完成后,调用http ack接口,确认数据处理完成。

优点
    客户端可以依据自己的消费能力进行消费,可控性好

缺点
    毕竟是消费者去拉取消息,但是消费者怎么知道消息到了呢?所以只能去轮询,每过一段时间去pull一次。试想一下,如果mq中没有数据,消费者可能一致陷入循环, 一直返回空数据。 kafka本身采用的是pull模式,针对这一问题, Kafka 采用的是长轮询机制。

2、push模式
    生产者(Producer)发出的消息到达后,消息服务器(broker)将消息推送到消费者(consumer)。即消费者只需要提供http回调接口即可,处理完业务逻辑后,返回结果数据即可。

优点
    服务端主动推送给客户端,及时性很高

缺点
    当客户端消费能力远低于服务端生产能力,那么一旦服务端推送大量消息到客户端时,就会导致客户端消息堆积,处理缓慢,甚至服务崩溃。(那么如何解决这个问题呢?需要mq提供流控制,也就是依据客户端消费能力做流控)
    服务端需要维护每次传输状态,以防消息传递失败进行重试

3、Polling(轮询)和 Long Polling(长轮询)

  • Polling:无论broker数据是否有更新,Consumer每隔一段时间去请求pull一次数据,结果是可能有数据返回,或返回空。
  • Long Polling:Consumer发起Long Polling后,此时如果broker没有数据,那么不会立刻返回结果(即broker保存相关请求,先不关闭请求连接),等有了相关数据,或等一定时间超时才会返回结果。返回后,Consumer会则立刻发起下一次Long Polling。不难发现,该方式是对 pull 模式的优化,解决了pull模式数据通知不及时问题,同时也可以减少无效轮询的次数。

因此:
    如果Broker一直有可读消息,long-polling 就相当于执行间隔为 0 的 pull模式(因为Consumer每次收到结果就立刻发起下一次pull)。如果Broker没有可读消息,那么请求会被暂时阻塞,在产生了下一个可读消息或在请求“超时之前”响应请求给Consumer。
 

五、kafka高可用架构
    Kafka 里有一个概念叫“Topic”,暂且可以认为是一个数据集合。

    举个例子,如果现在有一份用户数据要写入 Kafka,你可以弄一个 Topic 叫做“user_log_topic”来写入用户数据。如果要把订单数据录人 Kafka,那可以弄一个 Topic 叫做“order_topic”来记录。

    那么可能会出现这样的情况,就用用户数据这个 Topic 举例,如果每天网里面写入几十 TB 的数据,都放一台机器上靠谱吗?

    明显不行,所以 Kafka 里有一个东西叫 Partition,就是把一个 Topic 数据集合拆分为多个数据分区,可以理解为数据分片,而每个 Partition 可以在不同的机器上储存部分数据。

这样一个大的数据集合就可以被分布式的存储在多台机器上了。如图:
在这里插入图片描述
    相信聪明的发现这种情况下可能会有一些问题,万一某台机器突然挂掉,那这台机器上的 Partition 管理的数据不就丢了吗?这咋搞?

    简单,多搞几个副本呗,就是每个 Partition 都搞一个副本放在其他的机器上,这样某台机器挂了,只是这台机器的 Partition 数据丢了而已,其他机器上还有呢。

    此外,如果一个 Partition 有很多副本,Kafka 会选举其中一个 Parititon 副本成为 Leader,然后其他的 Partition 副本是 Follower。

    只有 Leader 对外提供读写操作,其他Follower 只是从 Leader 同步数据。Leader如果突然宕机了,就会选举其他的 Follower 作为新的 Leader 对外提供读写服务,这不就是高可用架构么?

看图来感受下:
在这里插入图片描述
 
六、kafka如何保证消息顺序消费
    kafka集群无法做到消费者在消费的时候全局有序,因为在创建topic时,会设置不同的分区(partition)并存储在不同的服务器上,生产者生产的消息虽然是有序的,但是经过分区后会被分到不同的partition,每个partition里的数据是按顺序存储的,但这只是局部有序,并不是全局有序。

    比如生产者生产出100条数据之后,通过一定的分组策略存储到broker的partition中的时候,0到10这10条消息被存到了partition-0中,10到20这10条消息被存到了partition-1中,这样当消费者消费的时候从partition中取出消息并消费时便是无序的。

那么能否做到消费者在消费消息的时候全局有序呢?

① 多数情况下做不到全局有序。特殊情况下可以,比如partition只有一个。但是这样的话,就没有分布式和负载均衡一说了。因此,全局有序无法在kafka要实现的大数据的场景下做到。只能保证某一个partition内部消费的有序性。
② 在发送消息的时候指定key,那么这些消息就会被发送到指定的partition中,从而保证了有序性。

结论:针对一个topic里面的数据,只能做到partition内部有序,无法做到全局有序。消息在被追加到 partition时都会被分配一个特定的偏移量(offset)。Kafka 通过offset来保证消息在partition内的顺序性。
 

七、kafka如何保证消息不丢失
    要保证消息不丢失,就需要在消息传递的过程中任何一个环节都不能丢失,即生产者、kafka本身以及消费者都得保证不丢失。
 
1、 生产者

ack应答机制:

Kafka 提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡来配置不同参数。

ack=0:生产者在写入消息之前不会等待服务器的响应。一旦有问题生产者无法感知,消息就丢了(比如服务器没有写入磁盘就宕机了,造成数据丢失)。但这种方式也有好处,就是能以网络能够支持的最大速度发送消息,吞吐量高。

ack=1:这个是默认值,只要集群中的leader( 某个Parititon 副本,其余副本为follower)收到消息,生产者就会收到一个来自服务器的成功响应。如果消息无法到达leader(比如leader崩溃,新的leader还没被选举出来),生产者会收到一个错误响应,此时生产者会重发消息为了防止数据丢失。但是,还有一个特殊情况会导致数据丢失,就是生产者收到了写成功通知,但是此时刚好leader还没来的及同步数据到follower,leader就崩溃了,这会导致数据丢失。

ack=-1:只有当所有参与复制的节点(leader及其下的follower)都收到消息时,生产者才会收到一个来自服务器的成功响应,这种模式最安全,因为其可以保证不止一个服务器收到消息 。但是有一种情况,如果在 follower 同步完成后, broker 发送 ack 之前, leader 发生故障,那么会造成数据重复 。
 

2、kafka

什么情况下 Kafka 中写入数据会丢失呢?

    写入数据是往某个 Partition 的 leader 里写,然后那个 Partition 的 Follower 会从 Leader 同步数据。那么问题来了,现在有一条数据刚写入 Leader Partition,还没来得及同步给 Follower,Leader Partiton 所在机器就宕机了,此时就会选举这个Partition 的一个 Follower 作为新的 Leader 对外提供服务,那这时消费者就读不到刚才写入的那条数据了,造成数据丢失。

如何保证不丢失呢,需同时满足两个条件?

  • 每个 Partition 至少有 1 个 Follower 在 ISR 列表里,即跟上了 Leader 的数据同步。
  • 每次写数据时,要求至少写入 Partition Leader 成功的同时,还至少有一个 ISR 里的 Follower 也写入成功。

如果不满足上述条件,那就一直写入失败,让生产者不停的尝试重试,直到满足上述条件为止,才认为写入成功。

什么是ISR?

    简单来讲,就是kafka自动监控有哪些Follower 及时的跟上了 Leader 的数据同步的一种机制。

    kafka会自动给每个 Partition 维护一个 ISR 列表,列表里一定会有 Leader以及与 Leader 保持同步的Follower。就是只要某个 Follower 一直能跟其leader保持数据同步,那么它们就会存在于 ISR 列表。但是如果 Follower自身出现问题,导致不能及时与 其Leader 同步,那这个 Follower 会被认为是“out-of-sync”,就会从 ISR 列表里被踢出去。
 
什么是OSR?
上面说到能跟上 Leader 数据同步的 Follower 会被保存在 ISR 列表中,那那些没有跟上 Leader 数据同步(数据滞后)的 Follower 呢?它们会被保存在一个叫 OSR 的列表中。
 
那么你可能会问了,如果 Leader 出现故障,怎么选举新 Leader 呢?
诶,上面的 ISR 不就用上了么,我们知道 ISR 列表中维护了与 Leader 同步的 所有 Follower(包含Leader),如果 Leader 出现问题,那么它会从 ISR 列表中随机挑选一个 Follower 作为新 Leader,那么问题来了,如果 ISR 列表中所有副本都丢失了怎么办?此时可以有两种解决方法:
(1)等待 ISR 列表中副本任何一个恢复,之后再对外提供服务,需要时间等待;
(2)可以从 OSR 中选择一个副本当 Leader 继续对外提供服务,但是有数据丢失的风险;
具体采取哪种策略,看具体场景需要。

 

3、消费者
在这里插入图片描述
什么时候消费者会发生消息丢失?

    假设前一次poll操作拉取的消息集是 [x+2,x+7],x+2 表示上一次提交的消费位移,此时说明已经完成了x+1之前(包括x+1在内)的所有消息的消费,x+5表示当前正在处理的位置。如果拉取到消息之后就立刻进行了位移提交,即提交了x+8,此时如果消费 x+5 出现了异常,在故障恢复之后,重新拉取的消息是肯定从 x+8 开始的。显然,x+5到x+7之间的消息丢失了,未能被消费。

    想要保证消息不丢失,可以将提交offset的方式改成手动方式,即每次消费完所拉取的消息之后再提交offset,这就可以避免拉取的消息还未消费便出先异常导致消息丢失的情况。

    但是这样做可能会导致重复消费的情况:因为位移提交动作是在消费完所有拉取到的消息之后才执行的,那么当消费x+5的时候遇到异常,故障恢复之后,重新拉取的消息是从x+2开始的。显然,x+2到x+4之间的消息重新消费了一遍。
 

八、kafka如何保证消息不重复消费

什么情况会导致重复消费?

1、自动提交offset造成重复消费
    自动提交并不是消费一条消息就提交一次offset,而是定期提交,定期的周期由客户端参数auto.commit.interval.ms配置,默认为5秒。假设刚提交完一次消费位移,然后拉取一批消息进行消费,在下一次自动提交消费位移之前,消费者崩溃了,那么又得从上一次位移提交的地方重新消费,发生了重复消费现象。

2、手动提交offset造成重复消费
    异步提交Offset的时候会有失败的情况发生,此时就会进行重试。如果某一次异步提交的消费位移为 x,但是提交失败了,然后下一次异步提交的消费位移为x+y,这次成功了。如果引入了重试机制,前一次的异步提交的消费位移在重试的时候提交成功了,那么此时的消费位移又变为了 x。恰巧此时发生故障,那恢复之后消费者就会从 x 处开始消费,这发生了重复消费现象。

如何解决?

落表(主键或者唯一索引的方式,避免重复数据) ,说白了就是先存起来呗,然后消费之前查一下有没有消费过,有就丢弃,没有就消费。

 
 
 
 
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值