1. Kafka 是什么?
Kafka
是⼀种⾼吞吐量、分布式、基于发布
/
订阅的消息系统,最初由
LinkedIn
公司开
发,使⽤
Scala
语⾔编写,⽬前是
Apache
的开源项⽬。
broker
:
Kafka
服务器,负责消息
存储和转发
topic
:消息类别,
Kafka
按照
topic
来分类消息
partition
:
topic
的分区,⼀
个
topic
可以包含多个
partition
,
topic
消息保存在各个
partition
上
offffset
:消息在⽇志
中的位置,可以理解是消息在
partition
上的偏移量,也是代表该消息的唯⼀序号
Producer
:消息⽣产者
Consumer
:消息消费者
Consumer Group
:消费者分组,每个
Consumer
必须属于⼀个
groupZookeeper
:保存着集群
broker
、
topic
、
partition
等
meta
数据;另外,还负责
broker
故障发现, partition leader 选举,负载均衡等功能

2.
Kafka
的设计时什么样的呢
Kafka
将消息以
topic
为单位进⾏归纳 将向
Kafka topic
发布消息的程序成为
producers.
将预订
topics
并消费消息的程序成为
consumer. Kafka
以集群的⽅式运⾏,可以由⼀个或
多个服务组成,每个服务叫做⼀个
broker. producers
通过⽹络将消息发送到
Kafka
集群,
集群向消费者提供消息。
3.
为什么要使⽤
kafka
,为什么要使⽤消息队列
?
缓冲和削峰
:上游数据时有突发流量,下游可能扛不住,或者下游没有⾜够多的机器来保
证冗余,
kafka
在中间可以起到⼀个缓冲的作⽤,把消息暂存在
kafka
中,下游服务就可以
按照⾃⼰的节奏进⾏慢慢处理。
解耦和扩展性
:项⽬开始的时候,并不能确定具体需求。消息队列可以作为⼀个接⼝层,
解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能⼒。
冗余
:可以采
⽤⼀对多的⽅式,⼀个⽣产者发布消息,可以被多个订阅
topic
的服务消费到,供多个毫⽆
关联的业务使⽤。
健壮性
:消息队列可以堆积请求,所以消费端业务即使短时间死掉,也
不会影响主要业务的正常进⾏。
异步通信
:很多时候,⽤户不想也不需要⽴即处理消息。
消息队列提供了异步处理机制,允许⽤户把⼀个消息放⼊队列,但并不⽴即处理它。想向
队列中放⼊多少消息就放多少,然后在需要的时候再去处理它们。
4.
数据传输的事物定义有哪三种?
数据传输的事务定义通常有以下三种级别:(
1
)最多⼀次
:
消息不会被重复发送,最多
被传输⼀次,但也有可能⼀次不传输 (
2
)最少⼀次
:
消息不会被漏发送,最少被传输⼀
次,但也有可能被重复传输
.
(
3
)精确的⼀次(
Exactly once
)
:
不会漏传输也不会重复
传输
,
每个消息都传输被⼀次⽽且仅仅被传输⼀次,这是⼤家所期望的
5.
Kafka
判断⼀个节点是否还活着有那两个条件?
(
1
)节点必须可以维护和
ZooKeeper
的连接,
Zookeeper
通过⼼跳机制检查每个节点
的连接 (
2
)如果节点是个
follower,
他必须能及时的同步
leader
的写操作,延时不能太久
6.
Kafka
中的
ISR
、
AR
⼜代表什么?
ISR
的伸缩⼜指什么
ISR:In-Sync Replicas
副本同步队列
AR:Assigned Replicas
所有副本
ISR
是由
leader
维护,
follower
从
leader
同步数据有⼀些延
迟(包括延迟时间
replica.lag.time.max.ms
和延迟条数
replica.lag.max.messages
两个维度
,
当前最新的版本
0.10.x
中只⽀持
replica.lag.time.max.ms
这个维度),任意⼀个超过阈值都
会把
follower
剔除出
ISR,
存⼊
OSR
(
Outof-Sync Replicas
)列表,新加⼊的
follower
也会先
存放在
OSR
中。
AR=ISR+OSR
。
7.
Kafka
中的
broker
是⼲什么的
broker
是消息的代理,
Producers
往
Brokers
⾥⾯的指定
Topic
中写消息,
Consumers
从
Brokers
⾥⾯拉取指定
Topic
的消息,然后进⾏业务处理,
broker
在中间起到⼀个代理保存
消息的中转站。
8.
producer
是否直接将数据发送到
broker
的
leader(
主节点
)
?
producer
直接将数据发送到
broker
的
leader(
主节点
)
,不需要在多个节点进⾏分发,为
了帮助
producer
做到这点,所有的
Kafka
节点都可以及时的告知
:
哪些节点是活动的,⽬
标
topic
⽬标分区的
leader
在哪。这样
producer
就可以直接将消息发送到⽬的地了
9.
什么情况下⼀个
broker
会从
isr
中踢出去
leader
会维护⼀个与其基本保持同步的
Replica
列表,该列表称为
ISR(in-sync Replica)
,每个
Partition
都会有⼀个
ISR
,⽽且是由
leader
动态维护 ,如果⼀个
follower
⽐⼀个
leader
落
后太多,或者超过⼀定时间未发起数据复制请求,则
leader
将其重
ISR
中移除 。
10.
Kafa consumer
是否可以消费指定分区消息?
Kafka consumer
消费消息时,向
broker
发出
"fetch"
请求去消费特定分区的消息,
consumer
指定消息在⽇志中的偏移量(
offset
),就可以消费从这个位置开始的消息,
customer
拥有了
offset
的控制权,可以向后回滚去重新消费之前的消息,这是很有意义
的
11.
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
阻塞知道新消息到
达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发)
12.
Kafka
存储在硬盘上的消息格式是什么?
消息由⼀个固定⻓度的头部和可变⻓度的字节数组组成。头部包含了⼀个版本号和
CRC32
校验码。
1.
消息⻓度
: 4 bytes (value: 1+4+n)
2.
版本号
: 1 byte 3CRC
3.
校验码
: 4 bytes
4.
具体的消息
: n bytes
13.
Kafka
⾼效⽂件存储设计特点:
(1).Kafka
把
topic
中⼀个
parition
⼤⽂件分成多个⼩⽂件段,通过多个⼩⽂件段,就容易
定期清除或删除已经消费完⽂件,减少磁盘占⽤。
(2).
通过索引信息可以快速定位
message
和确定
response
的最⼤⼤⼩。
(3).
通过
index
元数据全部映射到
memory
,可以避免
segment file
的
IO
磁盘操作。
(4).
通过索引⽂件稀疏存储,可以⼤幅降低
index
⽂件元数据占⽤空间⼤⼩。
14.
Kafka
与传统消息系统之间有三个关键区别
(1).Kafka
持久化⽇志,这些⽇志可以被重复读取和⽆限期保留
(2).Kafka
是⼀个分布式系
统:它以集群的⽅式运⾏,可以灵活伸缩,在内部通过复制数据提升容错能⼒和⾼可⽤性
(3).Kafka
⽀持实时的流式处理
15.
Kafka
创建
Topic
时如何将分区放置到不同的
Broker
中
(1)
.
副本因⼦不能⼤于
Broker
的个数;
(2)
.
第⼀个分区(编号为
0
)的第⼀个副本放置位
置是随机从
brokerList
选择的;
(3)
.
其他分区的第⼀个副本放置位置相对于第
0
个分区依
次往后移。也就是如果我们有
5
个
Broker
,
5
个分区,假设第⼀个分区放在第四个
Broker
上,那么第⼆个分区将会放在第五个
Broker
上;第三个分区将会放在第⼀个
Broker
上;第四个分区将会放在第⼆个
Broker
上,依次类推;
(4)
.
剩余的副本相对于第⼀
个副本放置位置其实是由
nextReplicaShift
决定的,⽽这个数也是随机产⽣的。
16.
Kafka
新建的分区会在哪个⽬录下创建
在启动
Kafka
集群之前,我们需要配置好
log.dirs
参数,其值是
Kafka
数据的存放⽬录,
这个参数可以配置多个⽬录,⽬录之间使⽤逗号分隔,通常这些⽬录是分布在不同的磁盘
上⽤于提⾼读写性能。当然我们也可以配置
log.dir
参数,含义⼀样。只需要设置其中⼀
个即可。如果
log.dirs
参数只配置了⼀个⽬录,那么分配到各个
Broker
上的分区肯定只
能在这个⽬录下创建⽂件夹⽤于存放数据。但是如果
log.dirs
参数配置了多个⽬录,那么
Kafka
会在哪个⽂件夹中创建分区⽬录呢?答案是:
Kafka
会在含有分区⽬录最少的⽂件夹
中创建新的分区⽬录,分区⽬录名为
Topic
名
+
分区
ID
。注意,是分区⽂件夹总数最少的
⽬录,⽽不是磁盘使⽤量最少的⽬录!也就是说,如果你给
log.dirs
参数新增了⼀个新的
磁盘,新的分区⽬录肯定是先在这个新的磁盘上创建直到这个新的磁盘⽬录拥有的分区⽬
录不是最少为⽌。
17.
partition
的数据如何保存到硬盘
topic
中的多个
partition
以⽂件夹的形式保存到
broker
,每个分区序号从
0
递增,且消
息有序
Partition
⽂件下有多个
segment
(
xxx.index
,
xxx.log
)
segment
⽂件⾥的 ⼤⼩
和配置⽂件⼤⼩⼀致可以根据要求修改 默认为
1g
如果⼤⼩⼤于
1g
时,会滚动⼀个新的
segment
并且以上⼀个
segment
最后⼀条消息的偏移量命名
18.
讲讲
kafka
维护消费状态跟踪的⽅法
⼤部分消息系统在
broker
端的维护消息被消费的记录:⼀个消息被分发到
consumer
后
broker
就⻢上进⾏标记或者等待
customer
的通知后进⾏标记。这样也可以在消息在消费
后⽴⻢就删除以减少空间占⽤。但是这样会不会有什么问题呢?如果⼀条消息发送出去之
后就⽴即被标记为消费过的, ⼀旦
consumer
处理消息时失败了(⽐如程序崩溃)消息就
丢失了。为了解决这个问题, 很多消息系统提供了另外⼀个个功能:当消息被发送出去之
后仅仅被标记为已发送状态,当接到
consumer
已经消费成功的通知后才标记为已被消费
的状态。这虽然解决了消息丢失的问题,但产⽣了新问题,⾸先如果
consumer
处理消息
成功了但是向
broker
发送响应时失败了,这条消息将被消费两次。第⼆个问题时,
broker
必须维护每条消息的状态,并且每次都要先锁住消息然后更改状态然后释放锁。这样麻烦
⼜来了,且不说要维护⼤量的状态数据,⽐如如果消息发送出去但没有收到消费成功的通
知,这条消息将⼀直处于被锁定的状态,
Kafka
采⽤了不同的策略。
Topic
被分成了若⼲分
区,每个分区在同⼀时间只被⼀个
consumer
消费。这意味着每个分区被消费的消息在⽇
志中的位置仅仅是⼀个简单的整数:
offffset
。这样就很容易标记每个分区消费状态就很容易
了,仅仅需要⼀个整数⽽已。这样消费状态的跟踪就很简单了。这带来了另外⼀个好处:
consumer
可以把
offffset
调成⼀个较⽼的值,去重新消费⽼的消息。这对传统的消息系统
来说看起来有些不可思议,但确实是⾮常有⽤的,谁规定了⼀条消息只能被消费⼀次呢?
19.
Kafka
的
ack
机制
request.required.acks
有三个值
0 1 -1 0:
⽣产者不会等待
broker
的
ack
,这个延迟最低但
是存储的保证最弱当
server
挂掉的时候就会丢数据
1
:服务端会等待
ack
值
leader
副本
确认接收到消息后发送
ack
但是如果
leader
挂掉后他不确保是否复制完成新
leader
也会
导致数据丢失
-1
:同样在
1
的基础上 服务端会等所有的
follower
的副本受到数据后才
会受到
leader
发出的
ack
,这样数据不会丢失
20.
Kafka
的消费者如何消费数据
消费者每次消费数据的时候,消费者都会记录消费的物理偏移量(
offset
)的位置等到下
次消费时,他会接着上次位置继续消费
21.
消费者负载均衡策略
⼀个消费者组中的⼀个分⽚对应⼀个消费者成员,他能保证每个消费者成员都能访问,如
果组中成员太多会有空闲的成员
22.
数据有序
⼀个消费者组⾥它的内部是有序的 消费者组与消费者组之间是⽆序的
23.
Kafka
⽣产数据时数据的分组策略
⽣产者决定数据产⽣到集群的哪个
partition
中 每⼀条消息都是以(
key
,
value
)格式
Key
是由⽣产者发送数据传⼊ 所以⽣产者(
key
)决定了数据产⽣到集群的哪个
partition
24.
Kafka
中的消息是否会丢失和重复消费?
要确定
Kafka
的消息是否丢失或重复,从两个⽅⾯分析⼊⼿:消息发送和消息消费。
1
、消息发送
Kafka
消息发送有两种⽅式:同步(
sync
)和异步(
async
),默认是同步⽅
式,可通过
producer.type
属性进⾏配置。
Kafka
通过配置
request.required.acks
属性来确认
消息的⽣产:
0---
表示不进⾏消息接收是否成功的确认;
1---
表示当
Leader
接收成功时确
认;
-1---
表示
Leader
和
Follower
都接收成功时确认;综上所述,有
6
种消息⽣产的情况,
下⾯分情况来分析消息丢失的场景:(
1
)
acks=0
,不和
Kafka
集群进⾏消息接收确认,则
当⽹络异常、缓冲区满了等情况时,消息可能丢失;(
2
)
acks=1
、同步模式下,只有
Leader
确认接收成功后但挂掉了,副本没有同步,数据可能丢失;
2
、消息消费
Kafka
消息
消费有两个
consumer
接⼝,
Low-level API
和
High-level API
:
Low-level API
:消费者⾃⼰维
护
offset
等值,可以实现对
Kafka
的完全控制;
High-level API
:封装了对
parition
和
offset
的管理,使⽤简单;如果使⽤⾼级接⼝
High-level API
,可能存在⼀个问题就是当消息消费
者从集群中把消息取出来、并提交了新的消息
offset
值后,还没来得及消费就挂掉了,那
么下次再消费时之前没消费成功的消息就
“
诡异
”
的消失了;解决办法:针对消息丢失:同
步模式下,确认机制设置为
-1
,即让消息写⼊
Leader
和
Follower
之后再确认消息发送成
功;异步模式下,为防⽌缓冲区满,可以在配置⽂件设置不限制阻塞超时时间,当缓冲区
满时让⽣产者⼀直处于阻塞状态;针对消息重复:将消息的唯⼀标识保存到外部介质中,
每次消费时判断是否处理过即可。
25.
Kafka
中是怎么体现消息顺序性的?
kafka
每个
partition
中的消息在写⼊时都是有序的,消费时,每个
partition
只能被每⼀个
group
中的⼀个消费者消费,保证了消费时也是有序的。整个
topic
不保证有序。如果为了
保证
topic
整个有序,那么将
partition
调整为
1.
26.
kafka
如何实现延迟队列?
Kafka
并没有使⽤
JDK
⾃带的
Timer
或者
DelayQueue
来实现延迟的功能,⽽是基于时间轮
⾃定义了⼀个⽤于实现延迟功能的定时器(
SystemTimer
)。
JDK
的
Timer
和
DelayQueue
插
⼊和删除操作的平均时间复杂度为
O(nlog(n))
,并不能满⾜
Kafka
的⾼性能要求,⽽基于时
间轮可以将插⼊和删除操作的时间复杂度都降为
O(1)
。时间轮的应⽤并⾮
Kafka
独有,其
应⽤场景还有很多,在
Netty
、
Akka
、
Quartz
、
Zookeeper
等组件中都存在时间轮的踪影。
底层使⽤数组实现,数组中的每个元素可以存放⼀个
TimerTaskList
对象。
TimerTaskList
是
⼀个环形双向链表,在其中的链表项
TimerTaskEntry
中封装了真正的定时任务
TimerTask.Kafka
中到底是怎么推进时间的呢?
Kafka
中的定时器借助了
JDK
中的
DelayQueue
来协助推进时间轮。具体做法是对于每个使⽤到的
TimerTaskList
都会加⼊到
DelayQueue
中。
Kafka
中的
TimingWheel
专⻔⽤来执⾏插⼊和删除
TimerTaskEntry
的操作,
⽽
DelayQueue
专⻔负责时间推进的任务。再试想⼀下,
DelayQueue
中的第⼀个超时任务
列表的
expiration
为
200ms
,第⼆个超时任务为
840ms
,这⾥获取
DelayQueue
的队头只需
要
O(1)
的时间复杂度。如果采⽤每秒定时推进,那么获取到第⼀个超时的任务列表时执⾏
的
200
次推进中有
199
次属于
“
空推进
”
,⽽获取到第⼆个超时任务时有需要执⾏
639
次
“
空
推进
”
,这样会⽆故空耗机器的性能资源,这⾥采⽤
DelayQueue
来辅助以少量空间换时
间,从⽽做到了
“
精准推进
”
。
Kafka
中的定时器真可谓是
“
知⼈善⽤
”
,⽤
TimingWheel
做最
擅⻓的任务添加和删除操作,⽽⽤
DelayQueue
做最擅⻓的时间推进⼯作,相辅相成。