kafka 设计的初衷
Kafka是最初由Linkedin开源出来的一套分布式消息系统
消息中间件: 将 数据方(producer) 生成的消息记录在 server, 供下游消费 (cosumer)
消息中间件的基本功能
解耦
扩展性
异步通信
保证顺序
峰值处理能力 & 缓冲
冗余
kafka 核心名词 及 解决方案
核心名词
Server 端
topic: 类似于一个具名消息队列,将所有发往此topic的消息追加到此队列
partition: 为了提高 topic 的响应能力,将一个 topic 水平切分多个队列 (同一个 topic 的消息的 hash 到不同的 partition)
replica: 冗余副本,每个 partition 有多个冗余,leader 故障时,快速补位。 (只有 leader 副本接受读写请求,普通副本只同步 leader 副本)
生产端
producer: 根据消费计算 partition id, 尽量将全部的消息 “合理”、“均匀”的分配到每个 partition
消费端
consumer: 指定 offset 主动消费消息, server 不关心其消费位置 (消费时,指定 topic,partition_id,offset,limit 从对应消息队列拉取数据)
订阅: consumer 订阅某个 topic 或者订阅 某个 partiton。订阅 partiton 将只会消费这些 partiton, 订阅 topic 系统将为其分配待消费的 partiton
consumer group: 为了便于管理,将 consumer 进行分组。同一个分组下订阅的全部 topic 的全部 partition 将被 ==均衡== 分配到本组的 consumer 上,partition 都会被消费,且只有一个 consumer 消费
散养的 consumer: 指定了自己要消费的 partition, 不受 group 控制
圈养的 consumer: 只是指定自己关注的 topic, 消费的 partition 只 group 分配
上述解决方案带来的特点
分区有序
有序,即先生成的消息优先被消息
consumer 针对 partition 消费,而同一个 topic 的消息分配到多个 partition 队列,消费时只能保证单个 partition 队列的有序, 不能保证 topic 级别的有序
producer 将消息合理地分配 partition,时序严格的消息应该放在同一个 partition。
比如处理用户信息,可以根据 member_id 计算 partition id
扩展与容灾
server 端:
增加分区实现水平扩展
提高副本数量实现容灾能力
consumer 端:
增加 group 中 consumner 实现水平扩展 (但数量超过 partition 后没有意义)
容灾,统一记录 offset 位置,consumer 宕机时,新 consumer 可以继续消费
散养的 consumer 的危害:
散养的 consumer 不受 group 限制
可以随意消费任意 partition,同时随意 commit 任意 partition 的 offset
处理不当会冲乱 offset
实现方案:
kafka 集群管理: partition/replica管理,topic管理
zk 方案
所有 broker 监听 zk,数据变化时做出相应变化。 (严重依赖 zk, 羊群效应、脑裂、zk 压力过大)
controller 方案
所有 broker 选举一个 controller 节点,controller 监听 zk, 处理完成后,通知相应 broker
副本容灾
leader 副本挂掉后,zk变化,controller 计算出新 leader
通知相应 broker(新 leader 开始工作,同时普通副本 follow新leader)
isr机制
简介: isr 列表,记录了当前所有“紧跟”leader的普通副本,当leader挂掉后,从这些副本中选择新 leader “基本不会”导致数据丢失
维护:判断普通副本 offset, 当跟上 leader 时自动加入 isr, 落后时自动退出,维护成本很低
优点:
向 leader 写入时,只要保证 isr 中副本被写入,即可认为数据写入成功
选择副本时只要 isr 不空,即可成功选取
容灾不依赖大多数策略。提高了写入与选举速度,减少了对副本数量的依赖
consumer group 如何均衡
zk 方案
所有 consumer 监听 zk 节点,数据变化时,重新分配 partition
羊群效应(节点变化所有 consumer 被触发)、脑裂(zk 最终一致,不同的 consumer 收到的信息不一定相同)
coordinator 方案:
kafka 为每个 group 起一个 consumerCoordinator 服务,将与 consumer 保证心跳。心跳断开时,consumerCoordinator 引导 consumer 完成新的分区分配
offset 记录:
可行方案:consumer 将 offset 记录到 DB; offset 记录在 zk; kafka server 将 offset 记录到特殊位置;……
提交方案:consumer 手动提交 offset, autoCommitTask 定期提交
默认方案
autoCommitTask 将每个 consumer 将 offset commit 到 consumerCoordinator
consumerCoordinator 将 offset 存到特殊的topic (offset topic 默认50个partition, 3个replica)
consumer 重新分配
consumerCoordinator 保持 consumer 心跳
consumerCoordinator 与 consumer 失联后,在所有活着的 consumer 选择一个 leader, 将分配任务交给 leader
leader 分配结束后,consumerCoordinator 将结果通知给所有 consumer
优化方案
数据传输,异步 io
producer: 每次新消息在 producer 端存储为队列,当数据到大一定量打包发送
顺序读写
将消息以 log 的形式顺序写入磁盘
zero-copy机制
将消息通过内核直接转发给 client
数据存储:分段、索引、打包、压缩、删除
分段:根据 offset 将队列拆成多个文件,记入消息时只追加到最新的文件
索引: 每个文件建立二分索引,可以根据 offset 快速定位到磁盘位置
每隔一定字节的数据建立一条索引,从而可以将索引文件保留在内存中
打包:将producer 发来的多条消息,打包成一个消息存入,consumner 消费时直接全扔过去 (数据延迟可能会变很大,导致consumer解析出错)
压缩
消息级压缩,将数据使用 zip 等格式压缩存储
文件级压缩,定期脚本,将相同 key 的数据合并成一条日志
删除:过期日志删除