三.Kafka
目录
5.列举kafka的优点,简述kafka为什么可以做到每秒数十万甚至上百万消息的高效分发?
10.kafka消息数据积压,kafka消费能力不足怎么处理?
13.当你使用kafka-topics.sh创建一个topic后,kafka背后会执行什么逻辑?
14.topic的分区数可以不可以增加?如果可以增加怎么增加?如果不可以那又是为什么?
15.topic的分区数可以不可以减少?如果可以增加怎么减少?如果不可以那又是为什么?
16.kafka有内部的topic吗?如果有是什么?有什么用?
29.kafka的每个分区只能被一个消费者线程,如何做到多个线程同时消费一个分区?
36.kafka创建topic时如何将分区设置到不同的broker中
40.kafka producer是如何动态感知主题分区数变化的?
43.如何为kafka集群选择合适的Topic/partitions数量
1.Kafka名词解释和工作方式
1)Producer :消息生产者,就是向 kafka broker 发消息的客户端;
2)Consumer :消息消费者,向 kafka broker 取消息的客户端;
3)Consumer Group (CG):消费者组,由多个 consumer 组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
4)Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker可以容纳多个 topic。
5)Topic :可以理解为一个队列,生产者和消费者面向的都是一个 topic;
6)Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列;
7)Replica:副本,为保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且 kafka 仍然能够继续工作,kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,一个 leader 和若干个 follower。
8)leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是 leader。
9)follower:每个分区多个副本中的“从”,实时从 leader中同步数据,保持和 leader 数据的同步。leader 发生故障时,某个 follower 会成为新的 follower。
2.Consumer与topic关系
本质上kafka只支持Topic;
每个group中可以有多个consumer,每个consumer属于一个consumer group;
通常情况下,一个group中会包含多个consumer,这样不仅可以提高topic中消息的并发消费能力,而且还能提高"故障容错"性,如果group中的某个consumer失效那么其消费的partitions将会有其他consumer自动接管。
对于Topic中的一条特定的消息,只会被订阅此Topic的每个group中的其中一个consumer消费,此消息不会发送给一个group的多个consumer;
那么一个group中所有的consumer将会交错的消费整个Topic,每个group中consumer消息消费互相独立,我们可以认为一个group是一个"订阅"者。
在kafka中,一个partition中的消息只会被group中的一个consumer消费(同一时刻);
一个Topic中的每个partions,只会被一个"订阅者"中的一个consumer消费,不过一个consumer可以同时消费多个partitions中的消息。
kafka的设计原理决定,对于一个topic,同一个group中不能有多于partitions个数的consumer同时消费,否则将意味着某些consumer将无法得到消息。
kafka只能保证一个partition中的消息被某个consumer消费时是顺序的;事实上,从Topic角度来说,当有多个partitions时,消息仍不是全局有序的。
3.kafka中生产数据的时候,如何保证写入的容错性?
设置发送数据是否需要服务端的反馈,有三个值0,1,-1
0: producer不会等待broker发送ack
1: 当leader接收到消息之后发送ack
-1: 当所有的follower都同步消息成功后发送ack
request.required.acks=0
4.如何保证kafka消费者消费数据是全局有序的
伪命题
每个分区内,每条消息都有一个offset,故只能保证分区内有序。
如果要全局有序的,必须保证生产有序,存储有序,消费有序。
由于生产可以做集群,存储可以分片,消费可以设置为一个consumerGroup,要保证全局有序,就需要保证每个环节都有序。
只有一个可能,就是一个生产者,一个partition,一个消费者。这种场景和大数据应用场景相悖。
5.列举kafka的优点,简述kafka为什么可以做到每秒数十万甚至上百万消息的高效分发?
优点:
高吞吐量:每秒数十万、上百万的高效分发。
持久化数据存储:将消息持久化到磁盘,因此可以用于批量消费,防止数据丢失。
分布式系统易于扩展:所有的producer、broker和consumer都会有多个,均为分布式的。无需停机即可扩展机器。
客户端状态维护:消息被处理的状态是在consumer端维护,而不是由server端维护。但失败时能自动平衡。
高效率 顺序写入/zero copy
虽然kafka会持久化所有数据到磁盘,当本质上每次写入操作其实都只是把数据写入到操作系统的页缓存,然后由操作系统自行决定什么时候把页缓存中的数据写回磁盘。
大量使用操作系统页缓存,内存操作速度快且命中率高。
Kafka 不直接参与物理 1/0 操作,而是交由最擅长此事的操作系统来完成。
采用追加写入方式,摒弃了缓慢的磁盘随机读/写操作。
使用以sendfile为代表的零拷贝技术加强网络间的数据传输率。
6.为什么离线分析要用kafka
kafka的作用是解耦,如果直接从日志服务器上采集的话,实时离线都要采集,等于要采集两份数据,而使用了kafka的话,只需要从日志服务器上采集一份数据,然后再kafka中使用不同的两个组读取就行了。
7.kafka怎么进行监控
使用kafka manager进行监控
下载kafka-manager。
并修改配置文件conf/application.conf,编写服务启动脚本。
启动集群及kafka manager,访问webui进行相关管理。
8.kafka与传统消息队列有什么不同
快速持久化,可以在O(1)的系统开销下进行消息持久化;
高吞吐,在一台普通的服务器上既可以达到10W/s的吞吐速率;
完全的分布式系统,Broker、Producer、Consumer都原生自动支持分布式,自动实现复杂均衡;
支持Hadoop数据并行加载,对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka通过Hadoop的并行加载机制来统一了在线和离线的消息处理
9.kafka的ISR副本同步队列
ISR(In-Sync Replicas)副本同步队列。ISR中包括leader和follower。
ISR 中的副本都是与 Leader 同步的副本,相反,不在 ISR 中的追随者副本就被认为是与 Leader 不同步的。
Leader 副本天然就在 ISR 中。
**ISR 是一个动态调整的集合,而非静态不变的。**如果同步时间超过replica.lag.time.max.ms则被认为不是同步的,应该踢出该副本存入OSR(Outof-Sync Replicas),如果同步时间在replica.lag.time.max.ms之内,则应该加入队列。
默认只有在队列里面的(被认定是实时同步的follower副本)才可能被选举为leader。(这个原则可以通过修改对应的参数配置来改变)
10.kafka消息数据积压,kafka消费能力不足怎么处理?
- 如果是kafka消费能力不足,则可以考虑增加topic的分区数,并且同时提升消费组的消费者数量,消费者数=分区数。
- 如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间<生产速度),使处理的数据小于生产的数据,也会造成数据积压。
11.kafka中ISR、OSR、AR代表什么?
ISR:In-Sync Replicas副本同步队列
OSR:Out-of-Sync Replicas同步超时队列
AR:Assigned Replicas所有副本。AR=ISR+OSR
LEO(Log End Offset):表示了当前日志文件中下一条待写入消息的offset。LEO的大小相当于当前日志分区中最后一条消息的offset值加1。
HW(High Watermak):表示了一个特定消息的偏移量(offset),消费者只能拉取到这个offset之前的消息。分区ISR集合中的每个副本都会维护自身的LEO,而ISR集合中最小的LEO即为分区的HW,对消费者而言只能消费HW之前的消息。
ps:
同步复制要求所有能工作的Follower副本都复制完,这条消息才会被确认为成功提交,这种复制方式影响了性能。
在异步复制的情况下, follower副本异步地从leader副本中复制数据,数据只要被leader副本写入就被认为已经成功提交。如果follower副本都没有复制完而落后于leader副本,如果突然leader副本宕机,则会造成数据丢失。
kafka使用ISR有效权衡了数据可靠性与性能之间的关系。
12 哪些情景会造成消息漏消费
先提交offset,后消费,有可能造成数据的重复
13.当你使用kafka-topics.sh创建一个topic后,kafka背后会执行什么逻辑?
在执行完脚本之后,Kafka 会在 log.dir 或 log.dirs 参数所配置的目录下创建相应的主题分区,默认情况下这个目录为/tmp/kafka-logs/。
在 ZooKeeper 的/brokers/topics/目录下创建一个同名的实节点,该节点中记录了该主题的分区副本分配方案。
触发Controller的监听程序
kafka Controller负责topic的创建工作,并更新metadata cache
14.topic的分区数可以不可以增加?如果可以增加怎么增加?如果不可以那又是为什么?
可以增加,使用 kafka-topics 脚本,结合 --alter 参数来增加某个主题的分区数,命令如下:
bin/kafka-topics.sh --bootstrap-server broker_host:port --alter --topic <topic_name> --partitions <新分区数>
当分区数增加时,就会触发订阅该主题的所有 Group 开启 Rebalance。
首先,Rebalance 过程对 Consumer Group 消费过程有极大的影响。在 Rebalance 过程中,所有 Consumer 实例都会停止消费,等待 Rebalance 完成。这是 Rebalance 为人诟病的一个方面。
其次,目前 Rebalance 的设计是所有 Consumer 实例共同参与,全部重新分配所有分区。其实更高效的做法是尽量减少分配方案的变动。
最后,Rebalance 实在是太慢了。
15.topic的分区数可以不可以减少?如果可以增加怎么减少?如果不可以那又是为什么?
不支持,因为删除的分区中的消息不好处理。
如果直接存储到现有分区的尾部,消息的时间戳就不会递增,如此对于 Spark、Flink 这类需要消息时间戳(事件时间)的组件将会受到影响;如果分散插入现有的分区,那么在消息量很大的时候,内部的数据复制会占用很大的资源,而且在复制期间,此主题的可用性又如何得到保障?与此同时,顺序性问题、事务性问题,以及分区和副本的状态机切换问题都是不得不面对的。
16.kafka有内部的topic吗?如果有是什么?有什么用?
__consumer_offsets,保存消费者offset
17.聊一聊kafka controller的作用
在Kafka集群中,每个Broker在启动时会实例化一个KafkaController类。该类会执行一系列业务逻辑,选举出主题分区的Leader节点,步骤如下:
- 第一个启动的代理节点,会在Zookeeper系统里面创建一个临时节点/controller,并写入该节点的注册信息,使该节点成为控制器;
- 其他的代理节点陆续启动时,也会尝试在Zookeeper系统中创建/controller节点,但是由于/controller节点已经存在,所以会抛出“创建/controller节点失败异常”的信息。创建失败的代理节点会根据返回的结果,判断出在Kafka集群中已经有一个控制器被成功创建了,所以放弃创建/controller节点,这样就确保了Kafka集群控制器的唯一性;
- 其他的代理节点,会在控制器上注册相应的监听器,各个监听器负责监听各自代理节点的状态变化。当监听到节点状态发生变化时,会触发相应的监听函数进行处理。
负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。
一 选举Leader和ISR
二 同步元数据信息包括broker和分区的元数据信息
三 broker增删监听与处理
四 topic变化监听与处理
五 分区变化监听与变化处理
18.失效副本是指什么?有哪些应对措施?
不能及时与leader同步,暂时踢出ISR,并加入OSR,等其追上leader之后再重新加入ISR。
19.kafka都有哪些特点
高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition,consumer group对partition进行consume操作。
可扩展性:kafka集群支持热扩展
持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)。
高并发:支持数千个客户端同时读写。
20.请简述你在那些场景下会选择kafka
-
日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、HBase、Solr等。
-
消息系统:解耦和生产者和消费者、缓存消息等。
-
用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
-
运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
-
流式处理:比如spark streaming和 Flink
21.kafka的设计架构
Producer :消息生产者,就是向 kafka broker 发消息的客户端。
Consumer :消息消费者,向 kafka broker 取消息的客户端。
Topic :可以理解为一个队列,一个 Topic 又分为一个或多个分区。
Consumer Group:这是 kafka 用来实现一个 topic 消息的广播(发给所有的 consumer)和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 Consumer Group。
Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。
Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker上,每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的id(offset)。将消息发给 consumer,kafka 只保证按一个 partition 中的消息的顺序,不保证一个 topic 的整体(多个 partition 间)的顺序。
Offset:kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查找。例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就是 00000000000.kafka。
22.kafka分区的目的
分区对于kafka集群的好处:实现负载均衡。
分区对于消费者来说,可以提高并发度,提高效率。
producer可以将数据发送给多个broker上的多个partition,consumer也可以并行从多个broker上的不同paritition上读数据,实现了水平扩展 。
由于消息是以追加的形式添加到到分区中的,多个分区顺序写磁盘的总效率比随机写内存还要高
23.kafka是如何做到消息的有序性
kafka中的每个partition中的消息在写入时都是有序的,而且消息带有offset偏移量,消费者按偏移量的顺序从前往后消费,从而保证了消息的顺序性。
但是分区之间的消息是不保证有序的。
24.kafka的高可靠性是怎么实现的
kafka通过分区的多副本机制来保证消息的可靠性。
每个分区可以设置多个副本,这些副本分布在不同的broker上;
相同partition的多个副本能动态选举leader来对外服务和管理内部数据同步。这样,即使有broker出现故障,受影响的partition也会在其它broker上重新选举出新的leader来继续服务。
25.kafka数据一致性原理
一致性就是说不论是老的 Leader 还是新选举的 Leader,Consumer 都能读到一样的数据。
所有在ISR中的副本都有个LEO(log end offset)偏移量,leader副本插入数据时,leaderLEO会增加,副本会复制leader新的数据,副本LEO也会增加,副本LEO不一样。
把ISR中的所有副本的最小LEO称为HW(high water mark),只有HW之前的数据才能被consumer消费。
producer端:
需要设置ack=all,如果发生leader出了问题,只有所有副本复制完成,producer才能写入成功,否则生产者会考虑重发消息。
consumer端:
因为consumer只能拉取HW之前的数据即ISR中所有副本都有的数据,所以如果此时发生leader选举,consumer不会拉取错误的数据,而是等到leader选举完成,HW发生变化,consumer才能重新消费。
使用HW这种模式兼顾了安全性和效率。当然主要还是用户按需求设置ack值。
26. kafka在什么情况下会出现消息丢失?
自动提交
设置offset为自动定时提交,当offset被自动定时提交时,数据还在内存中未处理,此时刚好把线程kill掉,那么offset已经提交,但是数据未处理,导致这部分内存中的数据丢失。
生产者发送消息
发送消息设置的是fire-and-forget(发后即忘),它只管往 Kafka 中发送消息而并不关心消息是否正确到达。不过在某些时候(比如发生不可重试异常时)会造成消息的丢失。这种发送方式的性能最高,可靠性也最差。
消费者端
先提交位移,但是消息还没消费完就宕机了,造成了消息没有被消费。自动位移提交同理
acks没有设置为all
如果在broker还没把消息同步到其他broker的时候宕机了,那么消息将会丢失
27.怎么尽可能保证kafka的可靠性
topic级别:replication-factor>=3;
producer级别:acks=-1;同时发送模式设置producer.type=sync;
broker级别:关闭不完全的leader选举,即unclean.leader.election.enable=false;
28.消费者和消费者组有什么关系
Consumer Group下可以有一个或多个Consumer实例。这里的实例可以是一个单独的进程,也可以是同一进程下的线程。在实际场景中,使用进程更为常见一些。
topic主题会将消息发给所有订阅了的组。组内的实例轮流获取消息。
如果要将一个消息多播,则一个consumer group对应一个consumer,每个consumer都能获取消息,将同一份数据发送到不同系统。如果只是想让一个消息单播,则一个consumer group对应多个consumer,每个消息只有一个consumer获取,常见于需要增加消费能力的场景。
29.kafka的每个分区只能被一个消费者线程,如何做到多个线程同时消费一个分区?
线程封闭,即为每个线程实例化一个kafkaconsumer对象。一个线程对应一个kafkaconsumer实例,称之为消费线程。一个消费线程可以消费一个或多个分区中的消息,所有的消费线程都隶属于同一个消费组。多线程+多kafkaconsumer实例
消费者程序使用单或多线程获取消息,同时创建多个消费线程执行消息处理逻辑。获取消息的线程可以是一个也可以是多个,每个线程维护专属的kafkaconsumer实例,处理消息则交由特定的线程池来做,从而实现消息获取与消息处理的真正解耦。单线程+单kafkaconsumer实例+消息处理worker线程池
30.kafka消费者是否可以消费指定分区消息?
可以。kafkaconsumer消费消息时,向broker发出fetch请求去消费特定分区的消息,consumer指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer拥有了offset的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的。
31.kafka消息是采用pull模式,还是push模式?
Kafka最初考虑的问题是,customer应该从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阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发
采用的pull模式。(producer将消息推送到broker,consumer从broker拉取消息。)
好处
consumer可以根据自己的消费能力进行消费,比如消费速率不一样,是否批量拉取数据。
缺点
如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到消息到达。为避免这点,kafka有参数可以让consumer阻塞直到新消息到达。(也可以阻塞直到消息数量达到某个特定的量,实现批量发)
32.kafka消息格式的演变
v0版消息格式(kafka 0.10之前的版本)
crc32(4B):crc32校验值。校验范围为magic至value之间。 magic(1B):消息格式版本号,此版本的magic值为0。 attributes(1B):消息的属性。总共占1个字节,低3位表示压缩类型:0表示NONE、1表示GZIP、2表示SNAPPY、3表示LZ4(LZ4自Kafka 0.9.x引入),其余位保留。 key length(4B):表示消息的key的长度。如果为-1,则表示没有设置key,即key=null。 key:可选,如果没有key则无此字段。 value length(4B):实际消息体的长度。如果为-1,则表示消息为空。 value:消息体。可以为空,比如tomnstone消息。
v1版本(从0.10.0版本开始到0.11.0版本之前的版本)
v1版本比v0版本多一个8B的timestamp字段;
timestamp字段作用:
内部而言:影响日志保存、切分策略;
外部而言:影响消息审计、端到端延迟等功能的扩展
v2版本(0.11.0版本及之后的版本)
相对v0和v1改动较大,引入了变长整形Varints和ZigZag编码。
Varints作用:根据数值的大小,调整占用的字节数,数值越小,占用的字节数就越小
0-63之间的数字占1个字节,64-8191之间的数字占2个字节,8192-1048575之间的数字占3个字节
kafka broker的配置message.max.bytes的默认大小为1000012(Varints编码占3个字节)
ZigZag编码:使绝对值较小的负数仍然享有较小的Varints编码值
V2版本消息集称为Record Batch(v0和v1称为Message Set),相较于V0、V1版本
(1)将多个消息(Record)打包存放到单个RecordBatch中,v2版本的单个Record Batch Header相较于v0、v1版本的多个Log_OVERHEAD(每个Record都会有1个LOG_OVERHEARD),会节省空间;
(2)引入变长整形Varints和ZigZag编码,能够灵活的节省空间
33.kafka偏移量的演变
Kafka 0.10.x 对于非压缩的消息偏移量处理和 Kafka 0.8.x 一致,这里就不再介绍了。这里主要介绍 Kafka 0.10.x 对压缩消息偏移量处理逻辑。和 Kafka 0.8.x 处理内部消息偏移量逻辑不一样,这个版本对于内部消息偏移量使用的是相对偏移量,从0开始,依次到n-1,这里的n代表压缩消息的条数。
这个逻辑和 Kafka 0.8.x 处理逻辑一致,不再介绍。有一点需要注意,Kafka 0.10.x 会将消息的 magic 值设置为 1,用于区分其他版本的消息,后面会介绍这样设置的用处。
Broker 端接收到 Producer 发送过来的压缩消息,其也是先解压接收到的压缩消息,然后做一堆的判断,比如 消息的 magic 值是否大于0,压缩消息内部的消息偏移量值是否连续(0,1,2,3这样的)等,如果符合这些条件(inPlaceAssignment = true),那么 Broker 会直接处理整个压缩消息外部的偏移量,内部消息的偏移量不需要设置,因为这个在 Producer 端已经设置好了;并不需要再次压缩消息,最后会将这条消息追加到 Log 文件中。
如果 inPlaceAssignment = false,这时候会直接操作解压后的消息,并给压缩消息内部消息设置偏移量,最后设置整个压缩消息的偏移量;这时候会忽略掉 Producer 端为压缩消息设置的偏移量,包括内部消息和整个压缩消息的偏移量。整个处理逻辑分为两种情况:
(1)如果接收到的消息不是由 Kafka 0.10.x 版本Producer客户端发送过来的,那么消息的 magic 值会等于0,这时候 Broker 设置偏移量逻辑和 Kafka 0.8.x 处理逻辑一致,也就是不管内部消息还是整个压缩消息的偏移量都是使用绝对偏移量;
(2)如果接收到的消息是由 Kafka 0.10.x 版本Producer客户端发送过来的,那么消息的 magic 值会等于1,这时候 Broker 会将压缩消息内部的消息偏移量设置成相对的,从0开始,依次到 n-1 ,最后整个压缩消息的偏移量为nextOffset + n - 1,其中n为压缩消息的条数。
偏移量设置完之后,对于inPlaceAssignment = false,不管是由什么版本发送过来的消息, Broker 需要重新压缩刚刚解压好的消息,最后会将这条消息追加到 Log 文件中。
Client端对于压缩消息偏移量处理
对不同版本的 Client 请求, Broker 会做出不同的判断:对于非 Kafka 0.10.x 版本的 Consumer,Broker 端消息的发送不会使用零拷贝技术;而如果是 Kafka 0.10.x 版本的 Consumer,Broker 端消息的发送才会使用零拷贝技术
34.数据传输的事务有几种?
数据传输的事务定义通常有以下三种级别:
-
最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输
-
最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.
-
精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都被传输
35.kafka高效文件存储设计特点
Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
通过索引信息可以快速定位message和确定response的最大大小。
通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小
36.kafka创建topic时如何将分区设置到不同的broker中
-
副本因子不能大于 Broker 的个数;
-
第一个分区(编号为0)的第一个副本放置位置是随机从 brokerList 选择的;
-
其他分区的第一个副本放置位置相对于第0个分区依次往后移。也就是如果我们有5个 Broker,5个分区,假设第一个分区放在第四个 Broker 上,那么第二个分区将会放在第五个 Broker 上;第三个分区将会放在第一个 Broker 上;第四个分区将会放在第二个 Broker 上,依次类推;
-
剩余的副本相对于第一个副本放置位置其实是由 nextReplicaShift 决定的,而这个数也是随机产生的;
37.kafka新建的分区会在哪个目录下创建
我们知道,在启动 Kafka 集群之前,我们需要配置好 log.dirs 参数,其值是 Kafka 数据的存放目录,这个参数可以配置多个目录,目录之间使用逗号分隔,通常这些目录是分布在不同的磁盘上用于提高读写性能。当然我们也可以配置 log.dir 参数,含义一样。只需要设置其中一个即可。
如果 log.dirs 参数只配置了一个目录,那么分配到各个 Broker 上的分区肯定只能在这个目录下创建文件夹用于存放数据。
但是如果 log.dirs 参数配置了多个目录,那么 Kafka 会在哪个文件夹中创建分区目录呢?答案是:Kafka 会在含有分区目录最少的文件夹中创建新的分区目录,分区目录名为 Topic名+分区ID。注意,是分区文件夹总数最少的目录,而不是磁盘使用量最少的目录!也就是说,如果你给 log.dirs 参数新增了一个新的磁盘,新的分区目录肯定是先在这个新的磁盘上创建直到这个新的磁盘目录拥有的分区目录不是最少为止。
38.kafka再均衡(Rebalance)
在Kafka中,当有新消费者加入或者订阅的topic数发生变化时,会触发Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance顾名思义就是重新均衡消费者消费。Rebalance的过程如下:
第一步:所有成员都向coordinator发送请求,请求入组。一旦所有成员都发送了请求,coordinator会从中选择一个consumer担任leader的角色,并把组成员信息以及订阅信息发给leader。第二步:leader开始分配消费方案,指明具体哪个consumer负责消费哪些topic的哪些partition。一旦完成分配,leader会将这个方案发给coordinator。coordinator接收到分配方案之后会把方案发给各个consumer,这样组内的所有成员就都知道自己应该消费哪些分区了。所以对于Rebalance来说,Coordinator起着至关重要的作用
39.kafka分区分配策略
每个 Topic 一般会有很多个 partitions。为了使得我们能够及时消费消息,我们也可能会启动多个 Consumer 去消费,而每个 Consumer 又会启动一个或多个streams去分别消费 Topic 对应分区中的数据。我们又知道,Kafka 存在 Consumer Group 的概念,也就是 group.id
一样的 Consumer,这些 Consumer 属于同一个Consumer Group,组内的所有消费者协调在一起来消费订阅主题(subscribed topics)的所有分区(partition)。当然,每个分区只能由同一个消费组内的一个consumer来消费。那么问题来了,同一个 Consumer Group 里面的 Consumer 是如何知道该消费哪些分区里面的数据呢?
Consumer1 为啥消费的是 Partition0 和 Partition2,而不是 Partition0 和 Partition3?这就涉及到 Kafka内部分区分配策略(Partition Assignment Strategy)了。
在 Kafka 内部存在两种默认的分区分配策略:Range 和 RoundRobin。当以下事件发生时,Kafka 将会进行一次分区分配:
-
同一个 Consumer Group 内新增消费者
-
消费者离开当前所属的Consumer Group,包括shuts down 或 crashes
-
订阅的主题新增分区
将分区的所有权从一个消费者移到另一个消费者称为重新平衡(rebalance),如何rebalance就涉及到本文提到的分区分配策略。下面我们将详细介绍 Kafka 内置的两种分区分配策略。本文假设我们有个名为 T1 的主题,其包含了10个分区,然后我们有两个消费者(C1,C2)来消费这10个分区里面的数据,而且 C1 的 num.streams = 1,C2 的 num.streams = 2。
Range strategy
Range策略是对每个主题而言的,首先对同一个主题里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。在我们的例子里面,排完序的分区将会是0, 1, 2, 3, 4, 5, 6, 7, 8, 9;消费者线程排完序将会是C1-0, C2-0, C2-1。然后将partitions的个数除于消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区。在我们的例子里面,我们有10个分区,3个消费者线程, 10 / 3 = 3,而且除不尽,那么消费者线程 C1-0 将会多消费一个分区,所以最后分区分配的结果看起来是这样的:
C1-0 将消费 0, 1, 2, 3 分区C2-0 将消费 4, 5, 6 分区C2-1 将消费 7, 8, 9 分区
假如我们有11个分区,那么最后分区分配的结果看起来是这样的:
C1-0 将消费 0, 1, 2, 3 分区C2-0 将消费 4, 5, 6, 7 分区C2-1 将消费 8, 9, 10 分区
假如我们有2个主题(T1和T2),分别有10个分区,那么最后分区分配的结果看起来是这样的:
C1-0 将消费 T1主题的 0, 1, 2, 3 分区以及 T2主题的 0, 1, 2, 3分区C2-0 将消费 T1主题的 4, 5, 6 分区以及 T2主题的 4, 5, 6分区C2-1 将消费 T1主题的 7, 8, 9 分区以及 T2主题的 7, 8, 9分区
可以看出,C1-0 消费者线程比其他消费者线程多消费了2个分区,这就是Range strategy的一个很明显的弊端。
RoundRobin strategy
使用RoundRobin策略有两个前提条件必须满足:
-
同一个Consumer Group里面的所有消费者的num.streams必须相等;
-
每个消费者订阅的主题必须相同。
所以这里假设前面提到的2个消费者的num.streams = 2。RoundRobin策略的工作原理:将所有主题的分区组成 TopicAndPartition 列表,然后对 TopicAndPartition 列表按照 hashCode 进行排序,这里文字可能说不清,看下面的代码应该会明白:
val allTopicPartitions = ctx.partitionsForTopic.flatMap { case(topic, partitions) =>
info("Consumer %s rebalancing the following partitions for topic %s: %s"
.format(ctx.consumerId, topic, partitions))
partitions.map(partition => {
TopicAndPartition(topic, partition)
})
}.toSeq.sortWith((topicPartition1, topicPartition2) => {
/*
* Randomize the order by taking the hashcode to reduce the likelihood of all partitions of a given topic ending
* up on one consumer (if it has a high enough stream count).
*/
topicPartition1.toString.hashCode < topicPartition2.toString.hashCode
})
最后按照round-robin风格将分区分别分配给不同的消费者线程。
在我们的例子里面,假如按照 hashCode 排序完的topic-partitions组依次为T1-5, T1-3, T1-0, T1-8, T1-2, T1-1, T1-4, T1-7, T1-6, T1-9,我们的消费者线程排序为C1-0, C1-1, C2-0, C2-1,最后分区分配的结果为:
C1-0 将消费 T1-5, T1-2, T1-6 分区;C1-1 将消费 T1-3, T1-1, T1-9 分区;C2-0 将消费 T1-0, T1-4 分区;C2-1 将消费 T1-8, T1-7 分区;
多个主题的分区分配和单个主题类似,这里就不在介绍了。
根据上面的详细介绍相信大家已经对Kafka的分区分配策略原理很清楚了。不过遗憾的是,目前我们还不能自定义分区分配策略,只能通过partition.assignment.strategy参数选择 range 或 roundrobin。partition.assignment.strategy参数默认的值是range。
40.kafka producer是如何动态感知主题分区数变化的?
在启动Kafka Producer往Kafka的Broker发送消息的时候,用户修改了该Topic的分区数,Producer可以在最多topic.metadata.refresh.interval.ms
的时间之后感知到,此感知同时适用于async
和sync
模式,并且可以将数据发送到新添加的分区中。
41.kafka是如何实现高吞吐率的?
Kafka是分布式消息系统,需要处理海量的消息,Kafka的设计是把所有的消息都写入速度低容量大的硬盘,以此来换取更强的存储能力,但实际上,使用硬盘并没有带来过多的性能损失。kafka主要使用了以下几个方式实现了超高的吞吐率:
-
顺序读写;
-
零拷贝
-
文件分段
-
批量发送
-
数据压缩。
42.kafka监控有哪些?
比较流行的监控工具有:
KafkaOffsetMonitor
KafkaManager
Kafka Web Console
Kafka Eagle
JMX协议(可以用诸如jdk自带的jconsole来进行连接获取状态信息)
43.如何为kafka集群选择合适的Topic/partitions数量
针对kafka 1.1.0以及之后的版本,建议单台broker上partition数量不超过4000, 整个集群partition数量不超过2000,000,主要原因还是上面讲过的controller选举和controller重新选举partition leader的耗时。
相对kafka 1.1.0之前版本,这个parition数量已经有了很大提高,这全部得益于controller处理broker shutdown流程的优化,主要是针对zk的写操作异步化,批量化,将新的metadata通知给没有shutdown的broker也批量化,减少RPC次数,但是最最主要的,大家肯定想不到,是减少了不必要的log, 具体可参考Apache Kafka Supports 200K Partitions Per Cluster
44.kafka事务
Kafka在0.11版本之前,Kafka不支持事务和幂等,只能保证数据不丢失,再在下游消费者对数据做全局去重。对于多个下游应用的情况,每个都需要单独做全局去重,这就对性能造成了很大影响。
0.11 版本的Kafka,在Producer引入幂等性。开启幂等性的 Producer在初始化的时候会被分配一个 PID,发往同一 Partition 的消息会附带 Sequence Number(消息序列化号)。而Broker 端会对做PID,Partition,SeqNumber缓存,当具有相同主键的消息提交时,Broker 只会持久化一条。但是 PID 重启就会变化,同时不同的Partition也具有不同主键,所以幂等性无法保证跨分区跨会话的 Exactly Once。在这种情况下,只保证了消息不重复发送给Kafka,是不是意味着消费者还是需要单独做去重?
同样,为实现跨分区跨会话的事务,kafka在0.11版还新增了事务型producer。引入了一个新的组件Transaction Coordinator。Producer 通过和 Transaction Coordinator交互获得 Transaction ID对应的任务状态。Transaction Coordinator还负责将所有事务写入Kafka的一个内部 Topic,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以得到恢复,从而继续进行。但是对于 Consumer 而言,事务的保证就会相对较弱,不同的 Segment File 生命周期不同,同一事务的消息可能会出现重启后被删除的情况。
参见这篇文章:Kafka Exactly Once语义与事务机制原理 | 技术世界 | kafka,大数据,集群,消息系统,郭俊 Jason,kafka 架构,kafka 事务,exactly once,正好一次
Kafka事务机制的实现主要是为了支持
Exactly Once即正好一次语义
操作的原子性
有状态操作的可恢复性
实现事务机制的几个阶段
幂等性发送
事务性保证
事务性消息传递
事务中Offset的提交
用于事务特性的控制型消息
事务过期机制
45.kafka幂等性
参见这篇文章:Kafka幂等性介绍与源码实现 - 简书
Kafka在0.11.0.0版本支持增加了对幂等的支持。幂等是针对生产者的特性。幂等可以保证生产者发送的消息,不会丢失,而且不会重复。
如何实现幂等
HTTP/1.1中对幂等性的定义是:一次和多次请求某一个资源对于资源本身应该具有同样的结果(网络超时等问题除外)。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
实现幂等的关键点就是服务端可以区分请求是否重复,过滤掉重复的请求。要区分请求是否重复的有两点:
唯一标识:要想区分请求是否重复,请求中就得有唯一标识。例如支付请求中,订单号就是唯一标识
记录下已处理过的请求标识:光有唯一标识还不够,还需要记录下那些请求是已经处理过的,这样当收到新的请求时,用新请求中的标识和处理记录进行比较,如果处理记录中有相同的标识,说明是重复交易,拒绝掉
Kafka幂等性实现原理
为了实现Producer的幂等性,Kafka引入了Producer ID(即PID)和Sequence Number。
PID。每个新的Producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。
Sequence Numbler。(对于每个PID,该Producer发送数据的每个<Topic, Partition>都对应一个从0开始单调递增的Sequence Number
Kafka可能存在多个生产者,会同时产生消息,但对Kafka来说,只需要保证每个生产者内部的消息幂等就可以了,所有引入了PID来标识不同的生产者。
对于Kafka来说,要解决的是生产者发送消息的幂等问题。也即需要区分每条消息是否重复。
Kafka通过为每条消息增加一个Sequence Numbler,通过Sequence Numbler来区分每条消息。每条消息对应一个分区,不同的分区产生的消息不可能重复。所有Sequence Numbler对应每个分区
Broker端在缓存中保存了这seq number,对于接收的每条消息,如果其序号比Broker缓存中序号大于1则接受它,否则将其丢弃。这样就可以实现了消息重复提交了。但是,只能保证单个Producer对于同一个<Topic, Partition>的Exactly Once语义。不能保证同一个Producer一个topic不同的partion幂等。
46.kafka缺点
-
由于是批量发送,数据并非真正的实时;
-
对于mqtt协议不支持;
-
不支持物联网传感数据直接接入;
-
仅支持统一分区内消息有序,无法实现全局消息有序;
-
监控不完善,需要安装插件;
-
依赖zookeeper进行元数据管理;
47.kafka新旧消费者的区别
旧的 Kafka 消费者 API 主要包括:SimpleConsumer(简单消费者) 和 ZookeeperConsumerConnectir(高级消费者)。SimpleConsumer 名字看起来是简单消费者,但是其实用起来很不简单,可以使用它从特定的分区和偏移量开始读取消息。高级消费者和现在新的消费者有点像,有消费者群组,有分区再均衡,不过它使用 ZK 来管理消费者群组,并不具备偏移量和再均衡的可操控性。
现在的消费者同时支持以上两种行为,所以为啥还用旧消费者 API 呢?
48.kafka分区数可以增加或者减少吗?为什么?
我们可以使用 bin/kafka-topics.sh 命令对 Kafka 增加 Kafka 的分区数据,但是 Kafka 不支持减少分区数。
Kafka 分区数据不支持减少是由很多原因的,比如减少的分区其数据放到哪里去?是删除,还是保留?删除的话,那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面?追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性,那么实现起来逻辑就会非常复杂。
49.kafka消息的存储机制
kafka通过 topic来分主题存放数据,主题内有分区,分区可以有多个副本,分区的内部还细分为若干个 segment。都是持久化到磁盘,采用零拷贝技术。
1、高效检索
分区下面,会进行分段操作,每个分段都会有对应的素引,这样就可以根据 offset二分查找定位到消息在哪一段,根据段的索引文件,定位具体的 mle ssage
2、分区副本可用性
leader选举,zk来协调
如果1eader宕机,选出了新的1eader,而新的 leader并不能保证已经完全同步了之前1eader的所有数据,只能保证HW(高水位设置)之前的数据是同步过的,此时所有的 follower都要将数据截断到HW的位置,再和新的 leader同步数据,来保证数据一致。
当宕机的 leader恢复,发现新的1eader中的数据和自己持有的数据不一致,此时宕机的1 eader会将自己的数据截断到宕机之前的hw位置,然后同步新1 eader的数据。宕机的1eader活过来也像 follower一样同步数据,来保证数据的一致性。
50.相比较于传统消息队列,kafka的区别
1、分区性:存储不会受单一服务器存储空间的限制
2、高可用性:副本leader选举
3、消息有序性:一个分区内是有序的。
4、负载均衡性:分区内的一条消息,只会被消费组中的一个消费者消费,主题中的消息,会均衡的发送给消费者组中的所有消费者进行消费。
51.消息丢失和消息重复
同步:这个生产者写一条消息的时候,它就立马发送到某个分区去。
异步:这个生产者写一条消息的时候,先是写到某个缓冲区,这个缓冲区里的数据还没写到 broker集群里的某个分区的时候,它就返回到 client去了
针对消息丢失:同步模式下,确认机制设置为-1,即让消息写入 Leader和 Fol lower之后再确认消息发送成功:
异步模式下,为防止缓冲区满,可以在配置文件设置不限制阻塞超时时间,当缓冲区满时让生产者一直处于阻塞状态
针对消息重复,将消息的唯一标识保存到外部介质中,每次消费时判断是否处理过即可