kafka剖析

[TOC]

kafka的架构

img

Producer: 消息生产者,向 Kafka Broker 发消息的客户端。

Consumer: 消息消费者,从 Kafka Broker 取消息的客户端。

Consumer Group: 多个消费者实例共同组成的一个组,同时消费多个分区以实现高吞吐。

重平衡:Rebalance。消费者组内某个消费者实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance 是 Kafka 消费者端实现高可用的重要手段。

Broker: 一台 Kafka 机器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。

Topic: 可以理解为一个队列,topic 将消息分类,生产者和消费者面向的是同一个 topic。

Partition(分区): 为了实现扩展性,提高并发能力,一个非常大的 topic 可以分布到多个 broker (即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个 有序的队列。

Replica: 副本,为实现备份的功能,保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且 Kafka 仍然能够继续工作,Kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,一个 leader 和若干个 follower。

Leader: 每个分区多个副本的“主”副本,生产者发送数据的对象,以及消费者消费数据的对象,都是 leader。

Follower: 每个分区多个副本的“从”副本,实时从 leader 中同步数据,保持和 leader 数据的同步。leader 发生故障时,某个 follower 还会成为新的 leader。

offset: 消费者消费的位置信息,监控数据消费到什么位置,当消费者挂掉再重新恢复的时候,可以从消费位置继续消费。

Zookeeper: Kafka 集群能够正常工作,需要依赖于 zookeeper,zookeeper 帮助 Kafka 存储和管理集群信息。

工作原理

Kafka集群将 Record 流存储在称为 topic 的类别中,每个记录由一个键、一个值和一个时间戳组成。Kafka 是一个分布式流平台,这到底是什么意思?

Kafka 工作流程

Kafka 中消息是以 topic 进行分类的,生产者生产消息,消费者消费消息,面向的都是同一个 topic。
topic 是逻辑上的概念,而 partition 是物理上的概念,每个 partition 对应于一个 log 文件,该 log 文件中存储的就是 Producer 生产的数据。Producer 生产的数据会不断追加到该 log 文件末端,且每条数据都有自己的 offset。消费者组中的每个消费者,都会实时记录自己消费到了哪个 offset,以便出错恢复时,从上次的位置继续消费。

存储机制

Kafka文件存储机制

由于生产者生产的消息会不断追加到 log 文件末尾,为防止 log 文件过大导致数据定位效率低下,Kafka 采取了分片索引机制,将每个 partition 分为多个 segment,每个 segment 对应两个文件:“.index” 索引文件和 “.log” 数据文件。

分区原因

方便在集群中扩展,每个 partition 可以通过调整以适应它所在的机器,而一个 topic 又可以有多个 partition 组成,因此可以以 partition 为单位读写了。

数据可靠性保证

为保证 producer 发送的数据,能可靠地发送到指定的 topic,topic 的每个 partition 收到 producer 发送的数据后,都需要向 producer 发送 ack(acknowledge 确认收到),如果 producer 收到 ack,就会进行下一轮的发送,否则重新发送数据。

img

副本数据同步策略

(1)何时发送 ack?
确保有 follower 与 leader 同步完成,leader 再发送 ack,这样才能保证 leader 挂掉之后,能在 follower 中选举出新的 leader 而不丢数据。
(2)多少个 follower 同步完成后发送 ack?
全部 follower 同步完成,再发送 ack。

同步策略方案比较

ISR

采用第二种方案,所有 follower 完成同步,producer 才能继续发送数据,设想有一个 follower 因为某种原因出现故障,那 leader 就要一直等到它完成同步。这个问题怎么解决?
leader维护了一个动态的 in-sync replica set(ISR):和 leader 保持同步的 follower 集合。当 ISR 集合中的 follower 完成数据的同步之后,leader 就会给 follower 发送 ack。如果 follower 长时间未向 leader 同步数据,则该 follower 将被踢出 ISR 集合,该时间阈值由 replica.lag.time.max.ms 参数设定。leader 发生故障后,就会从 ISR 中选举出新的 leader。

ack 应答机制

对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等 ISR 中的 follower 全部接受成功。
所以 Kafka 为用户提供了三种可靠性级别,用户根据可靠性和延迟的要求进行权衡,选择以下的配置。
(1)ack 参数配置:

  • 0:producer 不等待 broker 的 ack,这提供了最低延迟,broker 一收到数据还没有写入磁盘就已经返回,当 broker 故障时有可能丢失数据。
  • 1:producer 等待 broker 的 ack,partition 的 leader 落盘成功后返回 ack,如果在 follower 同步成功之前 leader 故障,那么将会丢失数据。
  • -1(all):producer 等待 broker 的 ack,partition 的 leader 和 follower 全部落盘成功后才返回 ack。但是在 broker 发送 ack 时,leader 发生故障,则会造成数据重复。

acks=-1 造成数据重复分析

故障处理细节

Log 文件中的 HW 和 LEO

LEO:每个副本最大的 offset。
HW:消费者能见到的最大的 offset。

高水位和 LEO 是副本对象的两个重要属性。Kafka 所有副本都有对应的高水位和 LEO 值,而不仅仅是 Leader 副本。只不过 Leader 副本比较特殊,Kafka 使用 Leader 副本的高水位来定义所在分区的高水位。换句话说,分区的高水位就是其 Leader 副本的高水位。

(1)Follower 故障
follower 发生故障后会被临时踢出 ISR 集合,待该 follower 恢复后,follower 会 读取本地磁盘记录的上次的 HW,并将 log 文件高于 HW 的部分截取掉,从 HW 开始向 leader 进行同步数据操作。等该 follower 的 LEO 大于等于该 partition 的 HW,即 follower 追上 leader 后,就可以重新加入 ISR 了。
(2)Leader 故障
leader 发生故障后,会从 ISR 中选出一个新的 leader,之后,为保证多个副本之间的数据一致性,其余的 follower 会先将各自的 log 文件高于 HW 的部分截掉,然后从新的 leader 同步数据。
注意:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。

作者:臧小晶
链接:https://juejin.im/post/6844904050064883725
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

kafka的特点

  高吞吐量:Kafka 每秒可以生产约 25 万消息(50 MB),每秒处理 55 万消息(110 MB)
  持久化数据存储:可进行持久化操作。将消息持久化到磁盘,因此可用于批量消费,例如 ETL,以及实时应用程序。通过将数据持久化到硬盘以及replication 防止数据丢失。
  分布式系统易于扩展:所有的 producer、broker 和 consumer 都会有多个,均为分布式的。无需停机即可扩展机器。
  客户端状态维护:消息被处理的状态是在 consumer 端维护,而不是由 server 端维护。当失败时能自动平衡。

Zookeeper在kafka中的作用?

kafka基于硬盘为什么这么快

顺序读写

众所周知Kafka是将消息记录持久化到本地磁盘中的.Kafka的message是不断追加到本地磁盘文件末尾的,而不是随机的写入,这使得Kafka写入吞吐量得到了显著提升。

Page Cache

为了优化读写性能,Kafka利用了操作系统本身的Page Cache

  • 首先,操作系统层面的缓存利用率会更高,因为存储的都是紧凑的字节结构而不是独立的对象。
  • 其次,操作系统本身也对于Page Cache做了大量优化,提供了write-behind、read-ahead以及flush等多种机制。再者,即使服务进程重启,系统缓存依然不会消失,避免了in-process cache重建缓存的过程。

通过操作系统的Page Cache,Kafka的读写操作基本上是基于内存的,读写速度得到了极大的提升。

就是利用操作系统自身的内存而不是JVM空间内存。这样做的好处有:

  • 避免Object消耗:如果是使用Java堆,Java对象的内存消耗比较大,通常是所存储数据的两倍甚至更多。
  • 避免GC问题:随着JVM中数据不断增多,垃圾回收将会变得复杂与缓慢,使用系统缓存就不会存在GC问题。

零拷贝

这里主要讲的是Kafka利用linux操作系统的 “零拷贝(zero-copy)” 机制在消费端做的优化。首先来了解下数据从文件发送到socket网络连接中的常规传输路径:

  • 操作系统从磁盘读取数据到内核空间(kernel space)的Page Cache
  • 应用程序读取Page Cache的数据到用户空间(user space)的缓冲区
  • 应用程序将用户空间缓冲区的数据写回内核空间到socket缓冲区(socket buffer)
  • 操作系统将数据从socket缓冲区复制到网络发送的NIC缓冲区

这个过程包含4次copy操作和2次系统上下文切换,性能其实非常低效。linux操作系统 “零拷贝” 机制使用了sendfile方法,允许操作系统将数据从Page Cache 直接发送到网络,只需要最后一步的copy操作将数据复制到 NIC 缓冲区,这样避免重新复制数据。示意图如下:
img

分区分段

Kafka的message是按topic分类存储的,topic中的数据又是按照一个一个的partition即分区存储到不同broker节点。每个partition对应了操作系统上的一个文件夹,partition实际上又是按照segment分段存储的。这也非常符合分布式系统分区分桶的设计思想。

通过这种分区分段的设计,Kafka的message消息实际上是分布式存储在一个一个小的segment中的,每次文件操作也是直接操作的segment。为了进一步的查询优化,Kafka又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度。

批量发送

Kafka允许进行批量发送消息,先将消息缓存在内存中,然后一次请求批量发送出去
比如可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去

和其他消息中间件的对比

吞吐量:10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 RabbitMQ比kafka低一个级别

可用性: 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用

RabbitMQ基于主从架构实现高可用

可靠性: 经过参数优化配置,可以做到 0 丢失

特性 ActiveMQ RabbitMQ RocketMQ Kafka
单机吞吐量 万级,比 RocketMQ、Kafka 低一个数量级 同 ActiveMQ 10 万级,支撑高吞吐 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
topic 数量对吞吐量的影响 topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
时效性 ms 级 微秒级,这是 RabbitMQ 的一大特点,延迟最低 ms 级 延迟在 ms 级以内
可用性 高,基于主从架构实现高可用 同 ActiveMQ 非常高,分布式架构 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性 有较低的概率丢失数据 基本不丢 经过参数优化配置,可以做到 0 丢失 同 RocketMQ
功能支持 MQ 领域的功能极其完备 基于 erlang 开发,并发能力很强,性能极好,延时很低 MQ 功能较为完善,还是分布式的,扩展性好 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用