Kafka实现原理阅读笔记
设计理念
持久化
- 尽量线性的读写磁盘。一个硬盘的顺序读写速度一般是4k读写的千倍以上。线性的读写是可以被预测,也能被操作系统大幅的优化的。
- 以pagecache为中心的设计风格,使用文件系统并依赖于pagecache要优于维护内存中缓存或其他结构。一方面避免 JVM 中的 gc带来的性能损耗。同时简化了代码实现。
- 持久化队列,只需要简单的在文件后面追加写入即可。而不用考虑建立一个索引文件(BTree)。查询和写入的复杂度由 BTree的 O(logN) 减小为线性的 O(1)。大幅提升数据的吞吐量,有利于处理海量数据,且对存储系统的性能要求不高,降低成本。
- 考虑将多条消息聚合在一次。减少平均每条消息的开销。
- 使用 zero-copy减少字符拷贝时候的开销。
- 开启压缩协议,减少数据所占的空间。
生产者 (Producer)
- Producer 向 Leader Partition 发送消息。
- Producer 可以向任何一个 Partition 询问整个集群的状态,以及谁是 Leader Partition
- Producer 自己决定写入到哪个 Partition。Producer 可以考虑使用何种负载的策略。随机,轮询,按照key分区都可以。
- 支持批量操作。消息攒够一定数量再发送,使用适当的延迟换来更高的数据吞吐量。
消费者 (Consumer)
- 消费者直接向 Leader Partition发送一个 fatch 的请求,并制定消费的起始位置(offset),取回offset后的一段数据进行处理。
- Consumer 自己决定 Offset,自己决定从什么地方进行消费。
- Push 和 Pull 的问题。 消息到底是推还是拉? kafka 采取的机制是,Producer 向 Broker push 消息。 Consumer 向 Broker Pull 消息。这样做有几个好处。第一,消息消费的速率由 Consumer自己决定。第二,可以聚合的数据批量处理数据,如果使用 push,Broker需要考虑到底要等到多条数据,还是及时发送,Consumer可以尽可能多的拉取数据,保证消息尽可能及时被消费。
- 如何记录那些消息被有效消费?Topic 被划分为多个有序的分区,保证每个分区任何时候只会被同一个Group里面的 Consumer消费。只需要记录消费的偏移量。同时这个位置可以作为CkeckPonit,定时检查。保证ACK的代价很小。
- 如果我们可以使用某个 Consumer 消费数据后,存储到 类似Hadoop的平台上持久化。
kafka 消息的语义
- 消息系统系统一般有以下的语义:
- At most once:消息可能丢失,但不会重复投递
- At least once:消息不会丢失,但可能会重复投递
- Exactly once:消息不丢失、不重复,会且只会被分发一次(真正想要的)
- Producer 发送消息以后,有一个commit的概念,如果commit成功,则意味着消息不会丢失,但是Producer有可能提交成功后,没有收到commit的消息。这有可能造成 at least once 语义。
- 从 Consumer 角度来看,我们知道 Offset 是由 Consumer 自己维护。所以何时更新 Offset 就决定了 Consumer 的语义。如果收到消息后更新 Offset,如果 Consumer crash,那新的 Cunsumer再次重启消费,就会造成 At most once 语义(消息会丢,但不重复)。
- 如果 Consumser 消费完成后,再更新 Offset。如果 Consumer crash,别的 Consumer 重新用这个 Offser 拉取消息,这个时候就会造成 at least once 的语义(消息不丢,但多次被处理)。
所以结论:默认Kafka提供at-least-once语义的消息分发,允许用户通过在处理消息之前保存位置信息的方式来提供at-most-once语义。如果我们可以实现消费是幂等的,这个时候就可以认为整个系统是Exactly once的了。
备份
- kafka 对每个 topic 的 partiotion 进行备份,份数由用户自己设置。
- 默认情况下 kafka 有一个 Leader 和 0至多个 Follower。
- 我们可以认为 Follower 也是一个 Consumer,通过消费 Leader 上的日志然后备份到本地。
- 所有的读写都是在 Leader 上进行的,所以 Follower 真的就只是备份。
- kafka 如何确认一个 Follower 是活的?
- 和 zookeeper 保持联系。
- Follower 复制 Leader 上的消息,且落后的不多(可配置)。
- 消息同步到所有的 Follower 才认为是提交成功,提交成功才能被消费。所以 Leader 宕机不会造成消息丢失(注意之前的Producer的 at least once 语义)。
选举
- Leader宕机以后,需要在Follower中选出一个新 Leader。 Kafak动态维护一个同步备份集合(ISR)。这个集合中的 Follower 都能成为 Leader。 一个写入,要同步到所有的 ISR 中才能算做 Commit 成功。同时 ISR 会被持久化到 ZK 中。
- 如果全部节点都故障了,kafka会选择第一副本(无需在ISR中) 作为Leader。这个时候会造成丢消息。
- Producer 可以选择是否等待备份响应。所谓的备份相应,是指 ISR 集合中的备份响应。