1、Kafka的消息流处理
1.1、消息路由
producer 发送消息到 broker 时,会根据分区算法选择将其存储到哪一个 partition。
其路由机制为:
1. 指定了 patition,则直接使用;
2. 未指定 patition 但指定 key,通过对 key 的 value 进行hash 选出一个 patition。
3. patition 和 key 都未指定,使用轮询选出一个 patition。
1.2、消息流程:
- producer要向kafka生产数据,需要先通过zookeeper获取leader的位置信息。
- Producer向leader发送数据。
- Leader收到数据后,将数据写入到分区目录下的log文件。
- Follower从leader同步数据,将数据写入到分区目录下的log文件中,如果同步成功(将数据写入自己log文件成功),则向leader返回ACK(确认机制)。
- leader向producer返回ACK。
细节补充:
Kafka引入了一个ISR机制(概念):
比如:①副本-follower ②副本-leader ③副本-follower
在follower和leader数据同步过程中,①②同步,③出现故障没有跟上,此时①②是同一个ISR成员,③不是。只有③恢复后,将数据同步成功,才会加入ISR成员中。
如果后续leader挂掉了,则kafka会从leader的ISR组中随机选择一个follower作为leader。
Kafka底层有一个同步超时时间(默认:10s),即一个follower在超时时间内没有反馈ACK,则认为同步失败。
补充概念说明:
ISR 的全称: In-Sync Replicas (同步副本集), 我们可以理解为和 leader 保持同步的所有副本的集合。
AR :Assigned Repllicas,一个分区的所有副本集合。OSR :Out-Sync Relipcas,与 leader-replica 未能保持同步的副本集。
因此我们就能得到这么一个表示:AR = ISR + OSR,翻译一下就是一个分区的副本集分为同步集合和非同步集合两部分。
【当所有副本都不工作时,有两种可行的方案】:
①等待ISR中的任意一个副本活过来,并选它作为leader。可保证数据不丢失,但时间可能相对较长。
②选择第一个活过来的副本(不一定是ISR成员)作为leader。无法保证数据不丢失,但相对不可用时间较短。
Kafka默认使用的是第②种。
2、Kafka的索引机制
Kafka为了提高数据的读取效率,引入了索引机制,为分区目录下的log文件创建一个对应的index文件。
一个log文件对应一个index索引文件,名字一样。
索引文件中包含若干个索引条目,每个条目表示数据文件中一条message的索引,即message在数据文件中的绝对位置。分析图如下:
Kafka的索引并不是为每条数据建立索引,这种索引称为 稀疏索引,因为稀疏索引占用的空间小,所以可以完全将稀疏索引加载到内存中,避免从磁盘上读取索引文件数据,减少磁盘I/O,进一步提高性能。
Kafka的索引机制可以概括为:稀疏索引 + 加载内存 + 二分查找
Kafka的每个log文件默认是1G生成一个新的log文件,比如 新的log文件中第一条消息的offset 16933,则此log文件的命名为:00000000000000016933.log。此外,每生成一个log文件,就会生成一个对应的索引(index)文件。这样在查找指定offset的message的时候,用二分查找 就可以定位到该message在哪个段中。
3、kafka数据传输的可靠性保障
数据传输的可靠性保障 共有三种:
- at most once 至多一次
- at least once 至少一次
- exactly once 精确一次
3.1、at most once 至多一次
如上图所示,在发送数据时,可能由于各种故障或异常,导致没有收到数据,即产生数据丢失。
对应的是 at most once 至多一次语义,即同一条数据至多只发送一次,所以这一层语义可能造成数据传输的丢失,也可以认为是没有可靠性保障的委婉说法。
3.2、at least once 至少一次
为了能确保数据不丢失,我们可以引入ACK确认机制 + 超时时间,如果在指定的超时时间内没有收到ACK,则认为接收失败,则将数据重新发送。
上图对应的语义:at least once 至少一次, 同一条数据至少发送一次,也可能发送多次,能确保数据不丢失,但可能会造成同一条数据的重复处理(比如在返回ACK时超时了,有这种可能,但在实际生产环境下概率较低)。
3.3、exactly once 精确一次
要想实现精确一次,需要在至少一次语义的基础上来实现,即确保数据不丢失(ACK+超时机制),此外,为数据分配一个全局唯一的id,利用这个id可以实现幂等性操作(幂等性操作指的是:代码执行多次和执行一次的结果是一样的)。从而避免同一条数据被重复处理。
即:exactly once 精确一次 :确保数据不丢失,且精确处理(不重复处理)。
3.4、总结
综上,这三层语义,没有好坏之分,主要看具体的应用场景。
例:
允许数据丢失,但追求高性能,使用 at most once 至多一次。
对数据精度要求非常高,一条不能丢并且结果严格正确,使用exactly once。
很多框架默认是第二种(折中的),如何降低数据重复处理的可能行呢?
可以适当调高超时时间,尤其是网络环境不好时。
3.5、Kafka中设置可靠性
在kafka中,可以通过配置文件来进行设定,在server.properties文件:
#至多一次
acks=0
#至少一次
acks=1 只接收leader的ACK ,这个常用。
acks=2 接收leader的ACK和一个Follower的ACK。
acks=3 接收leader的ACK和两个Follower的ACK。
#精确一次
acks=1
enable.idempotence=true
4、Zero copy (零拷贝技术)
将A服务上的文件,通过网络发送到B服务器上。
表面上一个简单的网络传输文件的过程,在OS(操作系统Operating System,简称OS)底层,会发现数据会被拷贝4次。 即:
- 磁盘 --- read buffer
- read buffer --- 应用缓存
- 应用缓存 --- socket buffer
- socket buffer --- NIC buffer 网卡缓存
内核态:CPU可以访问内存所有数据,包括外围设备,例如磁盘、网卡。
用户态:只能受限的访问内存,且不能访问外围设备。
上图中:
如果要对文件数据修改,则只能在用户态的缓冲区修改,所以需要拷贝4次。
但如果仅仅是发送文件数据,则 拷贝4次是没有意义的,并且还是产生4次内核态、用户态的切换,这些都需要耗费CPU的性能。
上图为最新zero copy。两次拷贝即可。并且没有内核态、用户态的交互次数。
综上,zero copy在通过网络传输文件数据时,可以减少拷贝次数、内核态和用户态的交互次数。
Kafka底层就是用了zero copy技术。
5、小结
Kafka的写入性能高:因为底层是磁盘的顺序写。
Kafka的读取性能高:因为底层是索引机制。
Kafka的传输性能高:因为底层使用了zero copy技术。