架构图

kafka不会自动创建topic kafka自动创建topic原理_服务器

Producer:Producer即生产者,消息的产生者,是消息的入口。

kafka cluster
Broker:Broker是kafka实例,每个服务器上有一个或多个kafka的实例,我们姑且认为每个broker对应一台服务器。每个kafka集群内的broker都有一个不重复的编号,如图中的broker-0、broker-1等……

  • Topic:消息的主题,可以理解为消息的分类,kafka的数据就保存在topic。在每个broker上都可以创建多个topic。
  • Partition:Topic的分区,每个topic可以有多个分区,分区的作用是做负载,提高kafka的吞吐量。同一个topic在不同的分区的数据是不重复的,partition的表现形式就是一个一个的文件夹!
  • Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的时候会选择一个备胎(Follower)上位,成为Leader。在kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自

Message:每一条发送的消息主体。

Consumer:消费者,即消息的消费方,是消息的出口。

Consumer Group:我们可以将多个消费组组成一个消费者组,在kafka的设计中同一个分区的数据只能被消费者组中的某一个消费者消费。同一个消费者组的消费者可以消费同一个topic的不同分区的数据,这也是为了提高kafka的吞吐量!

Zookeeper:kafka集群依赖zookeeper来保存集群的的元信息,来保证系统的可用性。

Partition的组成

Partition在服务器上的表现形式就是一个一个的文件夹,每个partition的文件夹下面会有多组segment文件,每组segment文件又包含.index文件、.log文件、.timeindex文件(早期版本中没有)三个文件, log文件就实际是存储message的地方,而index和timeindex文件为索引文件,用于检索消息。

kafka不会自动创建topic kafka自动创建topic原理_服务器_02

如上图,这个partition有三组segment文件,每个log文件的大小是一样的,但是存储的message数量是不一定相等的(每条的message大小不一致)。文件的命名是以该segment最小offset来命名的,如000.index存储offset为0~368795的消息,kafka就是利用分段+索引的方式来解决查找效率的问题。

存储策略

无论消息是否被消费,kafka都会保存所有的消息。那对于旧数据有什么删除策略呢?

  1. 基于时间,默认配置是168小时(7天)。
  2. 基于大小,默认配置是1073741824。

需要注意的是,kafka读取特定消息的时间复杂度是O(1),所以这里删除过期的文件并不会提高kafka的性能!

日志复制

Kafka 允许 topic 的 partition 拥有若干副本,你可以在server端配置partition 的副本数量。当集群中的节点出现故障时,能自动进行故障转移,保证数据的可用性。

创建副本的单位是 topic 的 partition ,正常情况下, 每个分区都有一个 leader 和零或多个 followers 。

所有的读写操作都由 leader 处理,一般 partition 的数量都比 broker 的数量多的多,各分区的 leader 均 匀的分布在brokers 中。所有的 followers 节点都同步 leader 节点的日志,日志中的消息和偏移量都和 leader 中的一致。(当然, 在任何给定时间, leader 节点的日志末尾时可能有几个消息尚未被备份完成)。

Followers 节点就像普通的 consumer 那样从 leader 节点那里拉取消息并保存在自己的日志文件中。Followers 节点可以从 leader 节点那里批量拉取消息日志到自己的日志文件中。

与大多数分布式系统一样,自动处理故障需要精确定义节点 “alive” 的概念。Kafka 判断节点是否存活有两种方式。

  1. 节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接。
  2. 如果节点是个 follower ,它必须能及时的同步 leader 的写操作,并且延时不能太久。

Kafka认为满足这两个条件的节点处于 “in sync” 状态,区别于 “alive” 和 “failed” 。 Leader会追踪所有 “in sync” 的节点。如果有节点挂掉了, 或是写超时, 或是心跳超时, leader 就会把它从同步副本列表中移除。 同步超时和写超时的时间由 replica.lag.time.max.ms 配置确定。

现在, 我们可以更精确地定义, 只有当消息被所有的副本节点加入到日志中时, 才算是提交, 只有提交的消息才会被 consumer 消费, 这样就不用担心一旦 leader 挂掉了消息会丢失。另一方面, producer 也 可以选择是否等待消息被提交,这取决他们的设置在延迟时间和持久性之间的权衡,这个选项是由 producer 使用的 acks 设置控制。 请注意,Topic 可以设置同步备份的最小数量, producer 请求确认消息是否被写入到所有的备份时, 可以用最小同步数量判断。如果 producer 对同步的备份数没有严格的要求,即使同步的备份数量低于 最小同步数量(例如,仅仅只有 leader 同步了数据),消息也会被提交,然后被消费。

ISR机制(一致性)

Kafka 动态维护了一个同步状态的备份的集合 (a set of in-sync replicas), 简称 ISR ,在这个集合中的节点都是和 leader 保持高度一致的,只有这个集合的成员才 有资格被选举为 leader,一条消息必须被这个集合 所有 节点读取并追加到日志中了,这条消息才能视为提交。这个 ISR 集合发生变化会在 ZooKeeper 持久化,正因为如此,这个集合中的任何一个节点都有资格被选为 leader 。这对于 Kafka 使用模型中, 有很多分区和并确保主从关系是很重要的。因为 ISR 模型和 f+1 副本,一个 Kafka topic 冗余 f 个节点故障而不会丢失任何已经提交的消息。

向 Kafka 写数据时,producers 设置 ack 是否提交完成, 0:不等待broker返回确认消息,1: leader保存成功返回或, -1(all): 所有备份都保存成功返回.请注意. 设置 “ack = all” 并不能保证所有的副本都写入了消息。默认情况下,当 acks = all 时,只要 ISR 副本同步完成,就会返回消息已经写入。

性能优化

顺序写磁盘

将写磁盘的过程变为顺序写,可极大提高对磁盘的利用率。Consumer通过offset顺序消费这些数据,且不删除已经消费的数据,从而避免随机写磁盘的过程。
Kafka删除旧数据的方式是删除整个Segment对应的log文件和整个index文件,而不是删除部分内容。

充分利用Page Cache(内核缓存)

相比于维护尽可能多的 in-memory cache,并且在空间不足的时候匆忙将数据 flush 到文件系统,我们把这个过程倒过来。所有数据一开始就被写入到文件系统的持久化日志中,而不用在 cache 空间不足的时候 flush 到磁盘。实际上,这表明数据被转移到了内核的 pagecache 中。

Page Cache的优点:

  1. I/O Scheduler会将连续的小块写组装成大块的物理写从而提高性能。
  2. I/O Scheduler会尝试将一些写操作重新按顺序排好,从而减少磁头移动时间。
  3. 充分利用所有空闲内存(非JVM内存)。
  4. 读操作可以直接在Page Cache内进行。如果消费和生产速度相当,甚至不需要通过物理磁盘交换数据。
  5. 如果进程重启,JVM内的Cache会失效,但Page Cache仍然可用。

零拷贝

Kafka中存在大量网络数据持久化到磁盘(Producer到Broker)和磁盘文件通过网络发送(Broker到Consumer)的过程,这个过程中传统模式下要进行数据的四次拷贝,Kafka通过零拷贝技术(sendfile)提交效率

减少网络开销

在某些情况下,数据传输的瓶颈不是 CPU ,也不是磁盘,而是网络带宽。对于需要通过广域网在数据中心之间发送消息的数据管道尤其如此。当然,用户可以在不需要 Kakfa 支持下一次一个的压缩消息。但是这样会造成非常差的压缩比和消息重复类型的冗余,比如 JSON 中的字段名称或者是或 Web 日志中的用户代理或公共字符串值。高性能的压缩是一次压缩多个消息,而不是压缩单个消息。

Kafka 以高效的批处理格式支持一批消息可以压缩在一起发送到服务器。这批消息将以压缩格式写入,并且在日志中保持压缩,只会在 consumer 消费时解压缩。

Kafka 支持 GZIP,Snappy 和 LZ4 压缩协议

参考