kafka原理及面试套路
- 一、 面试:
- 1、列举kafka的使用场景
- 2、Kafka消息是采用Pull模式,还是Push模式?
- 3、 Kafka 与传统消息系统之间有三个关键区别
- 4. Kafka中是怎么体现消息顺序性的?
- 5、Kafka生产者客户端中使用了几个线程来处理?分别是什么?
- 6、Kafka中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序是什么?
- 7、 “消费组中的消费者个数如果超过topic的分区,那么就会有消费者消费不到数据”这句话是否正确?如果不正确,那么有没有什么hack的手段?
- 8、有哪些情形会造成重复消费?
- 9、那些情景下会造成消息漏消费?
- 10、KafkaConsumer是非线程安全的,那么怎么样实现多线程消费?
- 11、当你使用kafka-topics.sh创建(删除)了一个topic之后,Kafka背后会执行什么逻辑?
- 12、优先副本是什么?它有什么特殊的作用?
- 13、Kafka 判断一个节点是否还活着有那两个条件?
- 14.producer 是否直接将数据发送到 broker 的 leader(主节点)?
- 15、Kafa consumer 是否可以消费指定分区消息?
- 16、kafka 的 ack 机制
- 17、kafka如何实现高吞吐
- 1、顺序读写
- 2、零拷贝
- 3、文件分段
- 4、批量发送
- 5、数据压缩
- 二、 kafka原理及底层机制
- 1、消息和批次
- 2、模式(schema)
- 3、主题和分区(topic和partition)
- 4、生产者和消费者
- 生产者:(发布者、写入者)
- 消费者(订阅者、读者)
- 消费者群组
- 5、broker和集群
- 6、kafka生产环境注意事项
- 垃圾回收器选择:(选择G1)
- 数据中心布局:
- 共享zookeeper
- 7、创建kafka生产者
- 8、生产者的配置
- 9、自定义序列化器
- 10、从kafka读取数据
- 11、kafka集群成员关系
- 12、控制器
- 13、kafka的复制
- 14、分区的副本类型
- 15、kafka集群的broker处理请求
- 16、消费请求
- 17、零复制技术
- 18、kafka文件格式
- 19、kafka的复制系数
- 20、kafka构建数据管道
- 21、kafka Streams 流处理
一、 面试:
1、列举kafka的使用场景
总结下来就几个字:异步处理、日常系统解耦、削峰、提速、广播
如果再说具体一点例如:消息,网站活动追踪,监测指标,日志聚合,流处理,事件采集,提交日志等
为什么选择Kafka?
吞吐量高,大数据消息系统唯一选择
2、Kafka消息是采用Pull模式,还是Push模式?
Kafka最初考虑的问题是,consumer应该从brokes拉取消息还是brokers将消息推送到consumer,也就是pull还push。在这方面,Kafka遵循了一种大部分消息系统共同的传统的设计:producer将消息推送到broker,consumer从broker拉取消息。
一些消息系统比如Scribe和Apache Flume采用了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阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发
3、 Kafka 与传统消息系统之间有三个关键区别
Kafka 持久化日志,这些日志可以被重复读取和无限期保留
Kafka 是一个分布式系统:它以集群的方式运行,可以灵活伸缩,在内部通过复制数据提升容错能力和高可用性
Kafka 支持实时的流式处理
4. Kafka中是怎么体现消息顺序性的?
kafka每个partition中的消息在写入时都是有序的,消费时,每个partition只能被每一个group中的一个消费者消费,保证了消费时也是有序的。
整个topic不保证有序。如果为了保证topic整个有序,那么将partition调整为1.
但是分区越少,吞吐量越小,你这样一个topic分1个区,是把kafka当消息队列来用。
5、Kafka生产者客户端中使用了几个线程来处理?分别是什么?
2个,主线程和Sender线程。
主线程负责创建消息,然后通过分区器、序列化器、拦截器作用之后缓存到累加器RecordAccumulator中。
Sender线程负责将RecordAccumulator中消息发送到kafka中.
6、Kafka中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序是什么?
分区器:根据键值确定消息应该处于哪个分区中,默认情况下使用轮询分区,可以自行实现分区器接口自定义分区逻辑
序列化器:键序列化器和值序列化器,将键和值都转为二进制流 还有反序列化器 将二进制流转为指定类型数据
拦截器:两个方法 doSend()方法会在序列化之前完成 onAcknowledgement()方法在消息确认或失败时调用 可以添加多个拦截器按顺序执行
调用顺序: 拦截器doSend() -> 序列化器 -> 分区器
7、 “消费组中的消费者个数如果超过topic的分区,那么就会有消费者消费不到数据”这句话是否正确?如果不正确,那么有没有什么hack的手段?
也对也不对。
因为通过自定义分区分配策略,可以将一个consumer指定消费所有partition。
但是如果你不去指定consumer的分区,就会出现有消费者消费不到数据。
因为一个分区partition为了确保消息的顺序性,只能有一个消费者。
8、有哪些情形会造成重复消费?
消费者消费后没有commit offset(程序崩溃/强行kill/消费耗时/自动提交偏移情况下unscrible)
9、那些情景下会造成消息漏消费?
消费者没有处理完消息 提交offset(自动提交偏移 未处理情况下程序异常结束)
10、KafkaConsumer是非线程安全的,那么怎么样实现多线程消费?
1.在每个线程中新建一个KafkaConsumer,每个线程的consumer都不是一个对象,就不会存在线程安全了。
2.单线程创建KafkaConsumer,要不直接把他搞个单例都可以,多个处理线程处理消息(难点在于是否要考虑消息顺序性,offset的提交方式)
11、当你使用kafka-topics.sh创建(删除)了一个topic之后,Kafka背后会执行什么逻辑?
创建:在zk上/brokers/topics/下节点 kafkabroker会监听节点变化创建主题
删除:调用脚本删除topic会在zk上将topic设置待删除标志,kafka后台有定时的线程会扫描所有需要删除的topic进行删除
12、优先副本是什么?它有什么特殊的作用?
优先副本 会是默认的leader副本 发生leader变化时重选举会优先选择优先副本作为leader
13、Kafka 判断一个节点是否还活着有那两个条件?
(1)节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连
接
(2)如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久
14.producer 是否直接将数据发送到 broker 的 leader(主节点)?
producer 直接将数据发送到 broker 的 leader(主节点),不需要在多个节点进行分发,为了
帮助 producer 做到这点,所有的 Kafka 节点都可以及时的告知:哪些节点是活动的,目标
topic 目标分区的 leader 在哪。这样 producer 就可以直接将消息发送到目的地了
15、Kafa consumer 是否可以消费指定分区消息?
Kafa consumer 消费消息时,向 broker 发出"fetch"请求去消费特定分区的消息,consumer
指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer 拥有
了 offset 的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的
16、kafka 的 ack 机制
request.required.acks 有三个值 0 1 -1
0:生产者不会等待 broker 的 ack,这个延迟最低但是存储的保证最弱当 server 挂掉的时候
就会丢数据
1:服务端会等待 ack 值 leader 副本确认接收到消息后发送 ack 但是如果 leader 挂掉后他
不确保是否复制完成新 leader 也会导致数据丢失
-1:同样在 1 的基础上 服务端会等所有的 follower 的副本受到数据后才会受到 leader 发出
的 ack,这样数据不会丢失
17、kafka如何实现高吞吐
kafka主要使用了以下几个方式实现了超高的吞吐率:
1、顺序读写
kafka的消息是不断追加到文件中的,这个特性使kafka可以充分利用磁盘的顺序读写性能顺序读写不需要硬盘磁头的寻道时间,只需很少的扇区旋转时间,所以速度远快于随机读写
2、零拷贝
先简单了解下文件系统的操作流程,例如一个程序要把文件内容发送到网络,这个程序是工作在用户空间,文件和网络socket属于硬件资源,两者之间有一个内核空间在操作系统内部,整个过程为:
在Linux kernel2.2 之后出现了一种叫做”零拷贝(zero-copy)”系统调用机制,就是跳过“用户缓冲区”的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到“用户态缓冲区”系统上下文切换减少为2次,可以提升一倍的性能
3、文件分段
kafka的队列topic被分为了多个区partition,每个partition又分为多个段segment,所以一个队列中的消息实际上是保存在N多个片段文件中通过分段的方式,每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力
4、批量发送
Kafka允许进行批量发送消息,先将消息缓存在内存中,然后一次请求批量发送出去比如可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去如100条消息就发送,或者每5秒发送一次这种策略将大大减少服务端的I/O次数
5、数据压缩
Kafka还支持对消息集合进行压缩,Producer可以通过GZIP或Snappy格式对消息集合进行压缩压缩的好处就是减少传输的数据量,减轻对网络传输的压力Producer压缩之后,在Consumer需进行解压,虽然增加了CPU的工作,但在对大数据处理上,瓶颈在网络上而不是CPU,所以这个成本很值得
二、 kafka原理及底层机制
我们通常用流这个词来描述kafka这类系统的数据,很多时候,人们把一个主题的数据看成一个流,不管这个主题有多少个分区。流是生产者流向消费者的数据。
1、消息和批次
kafka的数据单元被称为:消息。
消息可以看成是数据库里的一个“一行数据”或一条“记录”。
为了提高效率,消息被分批次写入kafka。这样可以减少网络开销。
批次就是一组消息,这组消息属于同一个主题(topic)和分区(partition)。批次数据会被压缩,以提升数据的网络传输。
2、模式(schema)
消息模式有许多,像xml和Json,可读性好,但是不同版本之间兼容不好。
kafka推荐使用Apache Avro,Avro提供了一种紧凑的序列化格式,模式和消息是分开的。
当模式发生变化时,不需要重新生成代码。
3、主题和分区(topic和partition)
kafka的消息数据通过主题进行分类。
主题好比是数据库的表,1个主题可以分成1个或者多个分区,1个分区就是一个提交日志。消息以追加方式写入分区,然后先入先出的顺序读取。
需要注意:由于1个主题,一般包含几个分区,因此无法保证在整个主题范围内消息的顺序。但可以保证消息在单个分区内的顺序;
kafka通过分区来实现数据冗余和伸缩性,分区可以分布在不同的服务器上。也就是说,一个主题通常横跨多个服务器,通过这种方式来提供单个服务器更强大的功能。
4、生产者和消费者
生产者:(发布者、写入者)
生产者默认情况下把消息均衡的分布到主题的所有分区上,而并不关系消息会被写到那个分区。
不过,在某些业务场景下,生产者需要把消息写到指定的分区上。
如何把消息写到指定分区?
通常使用通过消息键和分区器来实现,分区器为键生成一个散列值,并将其映射到指定的分区上。这样就可以保证“同一个键值”的消息会被写道同一个分区上。
消费者(订阅者、读者)
消费者通过订阅一个或多个主题,并按照分区上消息生成的顺序读取它们。
消费者通过检查消息的偏移量来区分已经读过的消息。
偏移量:是一种元数据,是一个不断递增的整数值,在创建消息时,kafka会把它添加到消息里。消费者把每个分区最后读取的消息偏移量保存在zookeeper或kafka上。如果消费者关闭或重启他的读取状态不会丢失。
消费者群组
消费者是消费者群组的一部分,因为会有1个或多个消费者共同读取一个主题。群组保证每个分区只能被一个消费者使用
如图:
上图说明,即分区数决定了同组消费者个数的上限
5、broker和集群
一个独立的kafka服务器被称为broker。
broker的工作:
接受生产者的消息,为消息设置偏移量,并提交消息到磁盘保护。
为消费者服务,对读取分区的请求作出响应,返回已经提交到磁盘上的消息。
6、kafka生产环境注意事项
垃圾回收器选择:(选择G1)
kafka的启动脚本并没有启用G1回收器,而是用了Parallel New 和 CMS垃圾回收器。
数据中心布局:
把kafka集群的broker安装在不同的机架上,至少不能让它们共享可能出现的单点故障
共享zookeeper
kafka使用zookeeper来保存broker、主题和分区的元数据信息。
7、创建kafka生产者
往kafka写消息,首先要创建一个生产者对象,并设置3个必选属性;
bootstrap.servers :
用于指定broker的地址清单,格式为host:port
建议至少提供2个broker的信息,一个宕机,生产者仍然可以连到集群上。
key.serializer
指定序列化器,该参数必须设置。
value.serializer
与key.serializer一样,value.serializer指定的类会将值序列化。
实例化对象后,就可以发送消息了。发送消息有以下3种方式。
发送并忘记(fire-and-forget)
把消息发送到服务器,但不关心它是否正常到达。该方式会丢失一些消息。
同步发送
我们使用send()方法发送消息,它会返回一个future对象。调用get()方法进行等待,就可以知道消息是否发送成功。
异步发送
我们使用send()方法,并指定一个回调函数,服务器在返回响应时调用该函数。
8、生产者的配置
acks参数指定了必须有多少个分区副本收到消息,生产者才会认为消息写入是成功的。
如果acks=0,生产者只管写消息,不用担心服务器是否真正写入。
如果acks=1,只要集群的leader节点收到消息,就表示写入成功。不必考虑副本节点。
如果acks=all,只有当所有参与复制的副本节点全部收到消息时,才表示成功写入。
9、自定义序列化器
kafka推荐使用Avro序列化,这是一种与编程语言无关的序列化方式。
当然还可以,使用业界比较有名的Google的Protobuf、Facebook的Thrift等;
10、从kafka读取数据
11、kafka集群成员关系
broker启动的时候,通过在zookeeper上创建临时节点把自己的id注册到zookeeper。
12、控制器
控制器其实也是一个broker,除了具备一般broker的功能外,还负责首领leader的选举。
kafka集群第一个启动的broker通过在zookeeper里创建一个临时节点 /controller 让自己成为控制器。
其它的broker在控制器的节点上创建Zookeeper watch对象,这样他们就可以收到这个节点变更的通知。这种方式保证集群一次只能有一个控制器controller存在。
简而言之,kafka使用zookeeper的临时节点来选举“控制器”。
13、kafka的复制
复制是kafka的核心功能。
在kafka的文档里,kafka把自己描述成“一个分布式的、可分区、可复制的提交日志的服务”。复制功能可以保证在个别节点失效时仍能保证kafka的可用性和持久性。
kafka使用主题来组织数据,每个主题分为若干个分区,每个分区有多个副本。每个broker可以保存成百上千个不同的主题和分区副本。
14、分区的副本类型
副本有2种类型:
首领副本
每个分区都有一个首领副本。为了保证数据的一致性,所有的生产者请求和消费都会经过这个副本。
追随者副本
首领以外的副本都是追随者副本。追随者副本不处理客户端的请求,唯一的任务就是从首领副本那里复制消息,保持与首领消息的一致状态。如果首领副本发生崩溃,其中的一个追随者副本会提升为新首领。
15、kafka集群的broker处理请求
broker的大部分工作是处理客户端、分区副本、和控制器发送给分区首领的请求。
broker按照请求到达的顺序处理请求,这种顺序保证kafka具有消息队列的特性,同时保证保存的消息也是有序。
生产请求和消费请求都必须发送给分区的首领副本
。
16、消费请求
客户端发送请求向broker请求主题分区里具有特定偏移量的消息。
同时客户端还可以指定broker最多一次可以从分区返回多少个数据,这个限制非常重要。
因为客户端要为broker返回的数据分配足够的内存,如果没有这个限制,broker返回的大量数据有可能耗尽客户端的内存。
如果请求成功,kafka使用零复制技术向客户端发送消息。
17、零复制技术
省去了数据在内核空间和用户空间来回拷贝,让数据传输不需要经过user space。
实现零复制技术有3种方式:
第一种方式,调用系统mmap函数。mmap函数基于内存映射的方式。缺点线程不安全,要加锁。
第二种方式,从2.1版内核开始,Linux引入了sendfile来简化操作;
使用sendfile不仅减少了数据拷贝的次数,还减少了上下文切换,数据传送始终只发生在kernel space。缺点是:sendfile只适用于将数据从文件拷贝到套接字上,限定了它的使用范围。
第三种方式,Linux在2.6.17版本引入splice系统调用,用于在两个文件描述符中移动数据。
SPLICE_F_MOVE :尝试去移动数据而不是拷贝数据。这仅仅是对内核的一个小提示:如果内核不能从pipe移动数据或者pipe的缓存不是一个整页面,仍然需要拷贝数据。Linux最初的实现有些问题,所以从2.6.21开始这个选项不起作用,后面的Linux版本应该会实现。
SPLICE_F_NONBLOCK:splice 操作不会被阻塞。然而,如果文件描述符没有被设置为不可被阻塞方式的 I/O ,那么调用 splice 有可能仍然被阻塞。
SPLICE_F_MORE: 后面的splice调用会有更多的数据。
splice调用利用了Linux提出的管道缓冲区机制, 所以至少一个描述符要为管道。
以上几种零拷贝技术都是减少数据在用户空间和内核空间拷贝技术实现的,但是有些时候,数据必须在用户空间和内核空间之间拷贝。这时候,我们只能针对数据在用户空间和内核空间拷贝的时机上下功夫了。Linux通常利用写时复制(copy on write)来减少系统开销,这个技术又时常称作COW。
18、kafka文件格式
我们把kafka的消息和偏移量保存在文件里。
kafka使用零复制技术给消费者发送消息,消息格式包括:键、值和偏移量,还包括消息大小、时间戳等。
19、kafka的复制系数
建议主题的复制系数为:3,也就是说每个分区总共会被3个不同的broker复制3次,因为kafka的默认复制系数就是3。银行金融业务中,建议复制系数为:5
20、kafka构建数据管道
建议使用Connect API 代替客户端API。
21、kafka Streams 流处理
kafka底层原理细节