您的位置:首页 > 职场人生

师兄大厂面试遇到面试官的 Kafka 暴击三连问,快面哭了!

2020-03-21 18:26 477 查看

文章目录

  • 五、关于 Kafka 和其他消息队列的区别
  • 六、Kafka 如何保证消息队列不丢失
  • 一、背景

    师兄最近依然在面试中,和我分享了很多面试题和经验,我都一一拿小本本记下来 ( 超认真 !!)🏃

    其实在今天的很多大厂面试里,都会要求能够熟练运用Apache Kafka等至少一种消息队列,Apache Kafka也是我们

    面试里的常客。在大多数人的印象中,写磁盘都是比较慢的,可是,为什么Apache Kafka在各大MQ性能的评测中,

    还能够击败众多对手,取得不错的成绩呢?

    通过师兄遇到的关于 Kafka 问题的暴击三连问 ,让我们一起走进师兄受伤的心 💔 …

    二、面试题

    1. Kafka 为什么快?
    2. Kafka 和其他消息队列的区别?
    3. Kafka 为什么这么快,又能保证不丢失消息?

    三、我的回答思路

    其实,在大数据开发岗的面试中,都避免不了 面试官问你 Kafka ,毕竟 Kafka 和 Kafka Stream 耍起来确实很香啊!

    我们就先从 Kafka 的 基础知识了解起吧,还不太了解的小伙伴可以先看看这几篇博客,先构建一个技术栈的知识框

    架,这样在面试时就能做到无懈可击啦! 💥

    Kafka
    【Kafka】(一)kafka 简介与设计、实现分析
    【Kafka】(十)Kafka 如何实现高吞吐量
    【Kafka】(十一)Kafka 的备份机制
    【Kafka】(十四)Kafka 架构深入

    四、关于 Kafka 为什么这么快

    Kafka 的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但

    是实际上,Kafka 的特性之一就是高吞吐率。Kafka 之所以能这么快,无非是:顺序写磁盘、大量使用内存页 、零

    拷贝技术的使用

    下面我就从数据写入和读取两方面分析,为大家分析下为什么 Kafka 速度这么快。(参考:阿里工程师告诉你 Kafka
    为什么这么快!)

    4.1 顺序写入

    数据写入

    Kafka 会把收到的消息都写入到硬盘中,它绝对不会丢失数据。为了优化写入速度 Kafka 采用了两个技术, 顺序写入和 MMFile(Memory Mapped File)。

    顺序写入

    磁盘读写的快慢取决于你怎么使用它,也就是顺序读写或者随机读写。在顺序读写的情况下,磁盘的顺序读写速度和内存持平。因为硬盘是机械结构,每次读写都会寻址->写入,其中寻址是一个“机械动作”,它是最耗时的。所以硬盘最讨厌随机 I/O,最喜欢顺序 I/O。为了提高读写硬盘的速度,Kafka 就是使用顺序 I/O。

    而且 Linux 对于磁盘的读写优化也比较多,包括 read-ahead 和 write-behind,磁盘缓存等。

    如果在内存做这些操作的时候,一个是 Java 对象的内存开销很大,另一个是随着堆内存数据的增多,Java 的 GC 时间会变得很长。

    使用磁盘操作有以下几个好处:

    • 磁盘顺序读写速度超过内存随机读写。

    • JVM 的 GC 效率低,内存占用大。使用磁盘可以避免这一问题。

    • 系统冷启动后,磁盘缓存依然可用。

    下图就展示了 Kafka 是如何写入数据的, 每一个 Partition 其实都是一个文件 ,收到消息后 Kafka 会把数据插入到文件末尾(虚框部分):


    这种方法有一个缺陷——没有办法删除数据 ,所以 Kafka 是不会删除数据的,它会把所有的数据都保留下来,每个
    消费者(Consumer)对每个 Topic 都有一个 Offset 用来表示读取到了第几条数据 。一般情况下 Offset 由客户端
    SDK 负责保存 ,会保存到 Zookeeper 里面 。关于存在硬盘中的消息,Kafka 也有它的解决方法,可以基于时间和
    Partition 文件的大小,正常 Kafka 是默认七天的保存,也可以通过命令来修改,以 users topic 为例。

    修改kafka 7天 默认保存周期
    kafka-topics.sh --zookeeper 6 --alter --topic users --config retention.ms=100000

    4.2 Memory Mapped Files

    这个和Java NIO中的内存映射基本相同,在大学的计算机原理里我们学过(划重点),mmf直接利用操作系统的Page来实现文件到物理内存的映射,完成之后对物理内存的操作会直接同步到硬盘。mmf通过内存映射的方式大大提高了IO速率,省去了用户空间到内核空间的复制。它的缺点显而易见–不可靠,当发生宕机而数据未同步到硬盘时,数据会丢失,Kafka提供了produce.type参数来控制是否主动的进行刷新,如果kafka写入到mmp后立即flush再返回给生产者则为同步模式,反之为异步模式。

    Kafka 提供了一个参数 producer.type 来控制是不是主动 Flush:

    • 如果 Kafka 写入到 mmap 之后就立即 Flush,然后再返回 Producer 叫同步 (Sync)。

    • 如果 Kafka 写入 mmap 之后立即返回 Producer 不调用 Flush 叫异步 (Async)。

    4.3 基于 Sendfile 实现零拷贝(Zero Copy)

    作为一个消息系统,不可避免的便是消息的拷贝,常规的操作,一条消息,需要从创建者的socket到应用,再到操作系统内核,然后才能落盘。同样,一条消息发送给消费者也要从磁盘到内核到应用再到接受者的socket,中间经过了多次不是很有必要的拷贝。

    传统 Read/Write 方式进行网络文件传输,在传输过程中,文件数据实际上是经过了四次 Copy 操作,其具体流程细节如下:

    • 调用 Read 函数,文件数据被 Copy 到内核缓冲区。

    • Read 函数返回,文件数据从内核缓冲区 Copy 到用户缓冲区

    • Write 函数调用,将文件数据从用户缓冲区 Copy 到内核与 Socket 相关的缓冲区。

    • 数据从 Socket 缓冲区 Copy 到相关协议引擎。

    硬盘—>内核 buf—>用户 buf—>Socket 相关缓冲区—>协议引擎

    而 Sendfile 系统调用则提供了一种减少以上多次 Copy,提升文件传输性能的方法。在内核版本 2.1 中,引入了 Sendfile 系统调用,以简化网络上和两个本地文件之间的数据传输。Sendfile 的引入不仅减少了数据复制,还减少了上下文切换。相较传统 Read/Write 方式,2.1 版本内核引进的 Sendfile 已经减少了内核缓冲区到 User 缓冲区,再由 User 缓冲区到 Socket 相关缓冲区的文件 Copy。而在内核版本 2.4 之后,文件描述符结果被改变,Sendfile 实现了更简单的方式,再次减少了一次 Copy 操作。

    Kafka 把所有的消息都存放在一个一个的文件中,当消费者需要数据的时候 Kafka 直接把文件发送给消费者,配合 mmap 作为文件读写方式,直接把它传给 Sendfile。

    4.4 批量发送

    Kafka允许进行批量发送消息,producter发送消息的时候,可以将消息缓存在本地,等到了固定条件发送到 Kafka 。

    • 等消息条数到固定条数。
    • 一段时间发送一次。

    4.5 数据压缩

    Kafka还支持对消息集合进行压缩,Producer可以通过GZIP或Snappy格式对消息集合进行压缩。
    压缩的好处就是减少传输的数据量,减轻对网络传输的压力。

    Producer压缩之后,在Consumer需进行解压,虽然增加了CPU的工作,但在对大数据处理上,瓶颈在网络上而不是CPU,所以这个成本很值得。

    注意:批量发送数据压缩一起使用,单条做数据压缩的话,效果不明显 ❗

    4.6 总结

    以上,便是Apache Kafka虽然使用了硬盘存储,但是仍然可以速度很快的原因。它把所有的消息都变成一个批量的文件,并且进行合理的批量压缩,减少网络 IO 损耗,通过 mmap 提高 I/O 速度。写入数据的时候由于单个 Partion 是末尾添加,所以速度最优;读取数据的时候配合 Sendfile 直接暴力输出。

    五、关于 Kafka 和其他消息队列的区别

    Kafka的设计目标是高吞吐量,那它与其它消息队列的区别就显而易见了:

    1、Kafka操作的是序列文件I / O(序列文件的特征是按顺序写,按顺序读),为保证顺序,Kafka强制点对点的按顺序传递消息,这意味着,一个consumer在消息流(或分区)中只有一个位置。

    2、Kafka不保存消息的状态,即消息是否被“消费”。一般的消息系统需要保存消息的状态,并且还需要以随机访问的形式更新消息的状态。而Kafka 的做法是保存Consumer在Topic分区中的位置offset,在offset之前的消息是已被“消费”的,在offset之后则为未“消费”的,并且offset是可以任意移动的,这样就消除了大部分的随机IO。

    3、Kafka支持点对点的批量消息传递。

    4、Kafka的消息存储在OS pagecache(页缓存,page cache的大小为一页,通常为4K,在Linux读写文件时,它用于缓存文件的逻辑内容,从而加快对磁盘上映像和数据的访问)。

    RabbitMQ:分布式,支持多种MQ协议,重量级
    ActiveMQ:与RabbitMQ类似
    ZeroMQ:以库的形式提供,使用复杂,无持久化
    Redis:单机、纯内存性好,持久化较差
    Kafka:分布式,消息不是使用完就丢失【较长时间持久化】,吞吐量高【高性能】,轻量灵活

    六、Kafka 如何保证消息队列不丢失

    我想,如果小伙伴直接遇到面试官问: Kafka 为什么这么快,又能保证不丢失消息?肯定是蒙蔽的,但如果前面回答的仅仅有条,这个时候再回答消息不丢失,就很容易啦 ! 👍

    6.1 关于 ACK 机制

    关于 ACK 机制 ,不了解的小伙伴,可以看这里:Kafka 架构深入 ,通过 ACK 机制保证消息送达。Kafka 采用的是至少一次(At least once),消息不会丢,但是可能会重复传输。

    acks 的默认值即为1,代表我们的消息被leader副本接收之后就算被成功发送。我们可以配置

    acks = all
    ,代表则所有副本都要接收到该消息之后该消息才算真正成功被发送。

    6.2 关于设置分区

    为了保证 leader 副本能有 follower 副本能同步消息,我们一般会为 topic 设置

    replication.factor >= 3
    。这样就可以保证每个 分区(partition) 至少有 3 个副本,以确保消息队列的安全性。

    6.3 关闭 unclean leader 选举

    我们最开始也说了我们发送的消息会被发送到 leader 副本,然后 follower 副本才能从 leader 副本中拉取消息进行同步。多个 follower 副本之间的消息同步情况不一样,当我们配置了

    unclean.leader.election.enable = false
    的话,当 leader 副本发生故障时就不会从 follower 副本中和 leader 同步程度达不到要求的副本中选择出 leader ,这样降低了消息丢失的可能性。

    6.4 关于发送消息

    为了得到更好的性能,Kafka 支持在生产者一侧进行本地buffer,也就是累积到一定的条数才发送,如果这里设置不当是会丢消息的。

    生产者端设置:

    producer.type=async, sync
    ,默认是 sync。

    当设置为 async,会大幅提升性能,因为生产者会在本地缓冲消息,并适时批量发送。

    如果对可靠性要求高,那么这里可以设置为 sync 同步发送。

    一般时候我们还需要设置:

    min.insync.replicas> 1
    ,消息至少要被写入到这么多副本才算成功,也是提升数据持久性的一个参数,与acks配合使用。

    但如果出现两者相等,我们还需要设置

    replication.factor = min.insync.replicas + 1
    ,避免在一个副本挂掉,整个分区无法工作的情况!

    6.5 Consumer 端丢失消息

    consumer端丢失消息的情形比较简单:

    如果在消息处理完成前就提交了offset,那么就有可能造成数据的丢失。

    由于Kafka consumer默认是自动提交位移的,所以在后台提交位移前一定要保证消息被正常处理了,因此不建议采用很重的处理逻辑,如果处理耗时很长,则建议把逻辑放到另一个线程中去做。

    为了避免数据丢失,现给出两点建议:

    enable.auto.commit=false

    关闭自动提交位移,在消息被完整处理之后再手动提交位移。

    以上,便是本篇博客的所有内容,希望师兄大厂面试顺利,这个系列还会继续更新一段时间,因为师兄还没有上岸 QAQ !

    • 点赞 4
    • 收藏
    • 分享
    • 文章举报
    云祁° 发布了343 篇原创文章 · 获赞 396 · 访问量 23万+ 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: