Kafka论文笔记

记录Kafka论文内容。

Kafka: a Distributed Messaging System for Log Processing

Kafka是一个高吞吐量低延时可伸缩分布式消息系统。

Other System

传统的消息系统一般作为消息总线,用于处理异步的数据流。

然而,这种消息总线的处理方式并不适合当下的日志处理的场景,究其原因在于:

① 传统的消息系统关注于消息投递,大量的日志会导致系统的过载。

在分布式场景下,消息投递的保证是通过Sync + ACK的方式来实现的。

因此,在传统消息系统中,消息投递过程是同步的,无法支持大量日志流的场景。

② 传统的消息系统大多都不是以吞吐量为作为首要目标,使用TCP作为消息投递的通信方式。

TCP是可靠的通信协议,并不适用于高吞吐量为目标的消息通信方式。

③ 传统的消息系统缺少分布式的支持

对于海量的日志消息,单机必然无法满足,多机的环境下的分区存储是不可避免的。

④ 传统的消息系统是建立在消息即时消费的假设之上,从而缺少对于大量消息持久化存储的能力。

对于海量的日志消息,消息的消费不一定是即时的,从而可能导致大量的消息堆积,

这也就是第③点的问题,如何通过分区来支持海量消息的堆积问题。


近几年海量日志处理的场景下产生一些日志聚合工具。

Scribe是Facebook用于收集日志数据的中间件,它基于Socket来收集日志数据,并通过日志聚合定期写入HDFS。

也就是说,Scribe是用于解决日志写入HDFS性能不足的一种手段。

Flume是Cloudera开发的日志聚合中间件,支持pipessinks的灵活扩展以及分布式的支持。

HedWig是Yahoo开发的一个分布式的发布订阅系统,主要用于记录消息的消费记录。

Architecture

Kafka引入了Broker用于消息的存储。

为了实现海量数据的存储,Kafka集群一般包含多个Broker。

为了实现负载均衡,每一个Topic会被分为多个Partition,每一个Broker会存储一个或多个Partion

Partition

Partition实际上是一个逻辑日志,它由于含多个Segment(物理日志)组成,每个Segment的大小约为1GB

每当一个生产者发布一条消息到某个Partition时,该条消息会追到到该Partition的最后一个Segment日志中。

为了实现高性能,Kafka先内存缓存消息再flush磁盘,刷盘策略有:

① 内存缓存到指定数量的消息后自动flush磁盘;

② 固定时间阈值自动flush磁盘。

在Kafka中,消息没有分配一个唯一固定的消息ID,而是采用一个基于日志的逻辑的偏移量来定位消息。

偏移量的使用可以避免磁盘的随机查找,通过偏移量可以准确的确定消息的位置。

对于消息的顺序写入,消息的定位不仅仅要记录消息的偏移量(Offset),还要记录消息的长度(Length)

偏移量(Offset)虽然是不连续的(记录消息的起始位置),但可以保证单调递增。

Kafka使用在内存维护了一个Offset的内存索引,记录了每个Segment内第一个消息的偏移量

高效传输

为了实现高效的数据传输,Kafka的设计如下,

消息的批量发送与拉取主要用于减少请求次数;

② 直接使用PageCache来避免的内存缓存;

③ 没有使用内存,因此避免了不必要的GC

④ 使用sendfile减少内存拷贝。

无状态的Broker

Broker是无状态的,其仅仅作为消息的存储,并不会保留任何Consumer的消费情况。

Broker的无状态特性可以降低消息系统的复杂性。

由于Broker的无状态,无法感知消息消费的情况,也就无法即时清除,因此一般采用定期清除策略来清除历史消息。

此外,基于Broker的无状态,Consumer可以实现消费的回退以及重试

Distributed Coordination

路由

消息投递到Partition的路由策略有:

① 随机选择:基于Random的随机选择;

② 哈希路由:基于PartitionKey的路由策略;

并行

Kafka使用Partition作为最小的并行处理单元

一个Partition仅能被一个Consumer消费,因此,Partition的数量要多于Consumer的数量。

协作

为了实现多个无状态的Broker之间的协作,引入了Zookeeper

① Broker的注册与监控

② 基于Zookeeper的监控来做rebalance

③ 维护Consumer的消费进度(Offert)

Broker/Consumer的注册是动态的(存在扩容与缩容的情况),使用ZK的临时节点

由于Broker是无状态的,因此Consumer的Offset的维护使用ZK的持久化节点

rebalance

当监听到ZK中Broker/Consumer的注册信息有变化时,对应的Consumer就会发起rebalance

rebalance是按照group来分组的,每个group维护着自己的Offset。

第一个监听到变化的Consumer会释放所有Partition再进行rebalance。

Partition的占用必然是通过ZK实现的。

Delivery Guarantees

Kafka仅支持至少一次的消息投递。

仅一次的消息投递需要两阶段提交(2PC)的支持,这与高吞吐量的目标不相符。

由于Partition是最小的并行处理单元,消息在Partition内部是有序的,

因此,通过PartitionKey的路由策略可以保证一定的顺序消费。

总结

本文仅是Kafka论文的记录,其内容与目前Kafka版本的实现可能有些不同。

本文还缺少对于Replication的介绍,会在后面的文章中有分析介绍。

对于Broker的无状态特性,Kafka后期并没有完全维持,例如,Consumer的Offset就从Zookeeper中迁移至Broker中。

Kafka作为独立的中间件,需要Zookeeper作为协调者来说有些太重了,因此社区正打算干掉ZK,例如,KIP-500打算把元数据都利用TOPIC来存储。

此外,随着Kafka的应用越来越广泛,消息投递的语义也不仅仅支持至少一次这种场景了,此时就需要考虑CAP理论了。