文章目录
- 细解 kafka 架构、应用场景及良好特性
-
- 什么是 kafka
- kafka 框架/组成
- kafka 数据流
-
- 消息数据写入 — 分区策略
-
- 轮训策略
- 随机策略
- 按 key 哈希策略
- 消息消费 — 分派策略
-
- rebalance
- range 策略
- 轮训策略
- 策略缺陷
- 消费者推拉模式
-
- push 模式
-
- 缺点
- 优点:
- pull 模式
- 协调者
-
- KIP-500
- consumer offset 迁移
- 吞吐量高且速度快
-
- 顺序读写
- 零拷贝
-
- sendfile
- mmap
- 读写
- 传统技术
-
- 读写
- 流式处理中扮演的角色
- 附录
细解 kafka 架构、应用场景及良好特性
文章涵盖角度较广,建议收藏备读!
了解更多,请关注 公众号 “ code 杂坛 “!
什么是 kafka
kafka 是一款由 Scala 和 Java 实现的,分布式流式处理平台。
因为具备高吞吐、可持久化、可水平扩展等特性而被广泛使用,也是软件开发人员的进阶必备技能!
本文就简单聊 关于 kafka 具体的内部结构、工作原理、应用场景都有哪些?
kafka 框架/组成
- kafka 的框架或组成是什么?
这个问题比较笼统,仔细一想 kafka 不就是存储 client 写入的数据,然后吐给 consumer,难道是问 里面的数据是如何组成的吗?其实不然。
放大来想,client 写入 和 consumer 消费也是 kafka 的一部分,那么可以这样回答:
kafka 可分为四部分:client 、代理[broker]、consumer、协调者[zookeeper]。
- client 写入数据到代理层[broker],代理层做数据的存储,然后分发给 consumer;最后一部分是统筹管理 kafka,扮演协调者的 zookeeper 。
了解更多,请关注 公众号 “ code 杂坛 “!
kafka 数据流
- kafka 中有这么几个概念:topic、partition、consumer、consumer group、broker……
其中 topic 是消息主题,属于逻辑概念,关联多个 partition[分区] ;partition 所属分布在不同的 broker[节点] 上;consumer 是消费者,以 group 区分为小组,小组可消费多个 topic…
数据流链路如下:
- client 将消息数据写入指定的 topic,实质写入 topic 下关联的多个 partition 中;
- partition 又可细分为多个体积一致的 segment 【partition 在分布式架构中会进行数据冗余的处理,确保数据/服务高可用,故会存在多个相同的 partition 分布在多个 broker 中】;
- segment 虽然体积一致,但消息条数可不同,可分为 .index、.log 两种类型文件且一一对应;
- partition 又可细分为多个体积一致的 segment 【partition 在分布式架构中会进行数据冗余的处理,确保数据/服务高可用,故会存在多个相同的 partition 分布在多个 broker 中】;
- broker 收到数据之后,进行解压缩等数据校验的操作后,会依据分发策略为 consumer 提供数据指派;
- consumer 依据自身的消费能力,去消费 topic 所在的 broker 拉取相关数据;
了解更多,请关注 公众号 “ code 杂坛 “!
消息数据写入 — 分区策略
- 既然 topic 是逻辑概念,数据是写入 partition 中,那么存在多个 partition 时,如何确认写入的 partition ?
kafka 除了支持消息写入时手动指定分区外,还提供了三种策略进行数据 partition 的负载均衡,分别是 轮训、随机、按 key 哈希。
轮训策略
轮训 是一种常规的均衡机制,可保证分区规模最大限度的均匀。
举个例子,若当前 topic 有 三个分区,partition 0、1、2;写入时就会依次写入 0、1、2……
随机策略
随机 是一种常规的均衡机制,其均匀性没 轮训策略程度高。
举个例子,若当前 topic 有 三个分区,partition 0、1、2;每次写入会在 0-2 之间随机出一分区进行写入操作……
按 key 哈希策略
哈希 是一种常规的均衡机制,可将相同 key 写入同一分区。
举个例子,若当前 topic 有 三个分区,partition 0、1、2;每次写入会对消息 key 进行
哈希;在同一分区中,消息具备保序特性;此策略可支持消息保序策略……
消息消费 — 分派策略
分派策略是指在消费者以群组形式消费时,partition 如何分派给消费实例的策略,触发策略生效最常见的时机就是 rebalance。
rebalance
- 我们先弄明白什么时候才会 rebalance ?
三种情况会触发 kafka 的 rebalance,分别是:
- 消费组实例数量发生变化:添加消费实例、实例下线
- 消费组订阅主题数量发生变化:消费组可通过正则匹配订阅主题
- 消费组订阅主题的分区数量发生变化:主题添加/删减分区
归结起来两部分:消费者规模变化 + 消费分区规模变化 都会导致消费组的 rebalance。
range 策略
range 是基于 区间分割的思想进行分派。
它会将单个 topic 的分区按顺序排列,将分区划分为 与消费者实例数量 一致的分块,存在余数的场景会额外分派给第一个消费实例 进行消费。
举个例子,topic-0 存在 5 个分区 0-4,2个消费实例 0-1。
依据 range 策略,consumer-0 将被分派 partition0-2,consumer-1 将被分派 partition3-4 ;
了解更多,请关注 公众号 “ code 杂坛 “!
轮训策略
轮训 是基于全部的 topic 分区 做依次分发 给到 消费者实例。
举个例子,topic-0/1 存在 2 个分区
topic0/1-partition-0、topic0/1-partition-1,2个消费实例 0-1。
依据 轮训策略,consumer-0 将被分派 topic0/1-partition-0,consumer-1 将被分派 topic0/1-partition-1;
策略缺陷
-
在 range 策略中,多 topic 主题下会导致数据的倾斜,单 topic 状态是 ok 的;
-
在 轮训 策略中,多 topic 主题下分区数量的差异会导致数据的倾斜,多 topic 分区规模一致状态是 ok 的;
为什么会这样呢?
单 topic 主题下,range 、轮训 策略状态如下:
多 topic 主题下分区数量的差异状态下,轮训策略状态如下:
消费者推拉模式
作为消息中间件,我们了解有两种消息处理方式:pull or push…
那么 kafka 消费者为什么就保留了 pull 模式呢?
push 模式
push 模式支持消息队列主动向下游推送消息。那么中间件需要知晓这些下游接收状态:
- 什么时候可以发送消息
- 哪些消息都是发送给哪些下游
- …
知道了这些状态,消息以来就可以立即发送给下游服务。
这样的机制存在些问题,优缺点十分明显。
缺点
- 从架构设计上来讲,
- 中间件与下游服务耦合严重;
- 中间件需保持下游服务状态,不利于承接大规模的下游服务;
- 从业务上来讲,
- 中间件无法预测发送速率,每个下游的消费能力不同,过大的窗口会压垮下游;太小的窗口,下游利用率不足;
优点:
消息处理实时性好;
pull 模式
pull 模式支持下游服务主动向中间件拉消息。
此模式良好的解决了 push 模式缺陷问题,但也缺失了其优点问题。除了 push 模式相关的特性,pull 模式还有额外的特点:
- 由于消息存在处理延迟问题,所以 pull 模式支持数据的聚合和批量拉取的特征;
kafka 支持了 pull 模式,也是其分布式高可扩展性的经典设计。从设计方案选型来讲,两种模式都有不同的优缺点,适用于各自的业务场景,当场出了这两种模式,逐渐还衍生出其他的模式,如 long-polling……等等,后续介绍,这里不做重点。
协调者
- 在 kafak 架构/组成中,存在 zookeeper 组件作为协调者。
那么协调者具体的作用是什么呢?或者说 kafka 中的 zookeeper 发挥了什么作用呢?
- kafka 主要利用 zookeeper 的共识算法,保证数据的一致性特点。
拆开来讲,- 存储元数据
zookeeper 存储了 kafka 集群的 broker集群信息、消费组信息、主题信息及分区、ISR 同步集合、… 等原数据; - 数据的一致性
zookeeper 不仅仅对数据做存储,还要动态更新数据、保障对外一致性; - Leader 选举
kafka 通过提供多副本机制完成数据的冗余,为服务高可用提供支持。其副本之间的选主策略由 zookeeper 提供实现,此功能也可以叫做 分布式锁;
- 存储元数据
KIP-500
KIP-500 是 kafka 社区正在逐步实现的一个提案,主旨是接触对 zookeeper 的依赖,为什么这样做呢?
总体概括有这么几点,
- 维护成本:需维护 kafka 、zookeeper 两个服务;
- 存储成本:数据割裂,主要元数据依赖外部服务且、分区及节点动态变更频繁时,zookpeer 压力大对 kafka 服务性能有影响;
- Leader 选举:大规模数据场景下,zookeeper 迁移 controll 时,存在 kafka 暂不可用问题;
KIP-500 用 Quorum Controller 代替之前的 Controller,Quorum 中每个 Controller 节点都会保存所有元数据,通过 KRaft 协议保证副本的一致性。
这样即使 Quorum Controller 节点出故障了,新的 Controller 迁移也会非常快。
consumer offset 迁移
KIP-500 提案,一些已经逐步落地。
比如,新版本中已经将 消费者提交的 offset 及 消费组信息 由原来的 zookeeper 替换至了 kafka 的 consumer_offset 主题存储….
在云原生的背景下,使用 Zookeeper 给 Kafka 的运维和集群性能造成了很大的压力。去除 Zookeeper 的必然趋势!
吞吐量高且速度快
上文了解了 kafka 构成组件的功能实现及目前社区发展趋势,那么为什么 kafak 大收市场青睐呢?
几十上百万的吞吐量及毫秒级的数据处理等特性是收市场欢迎的主要因素之一。
顺序读写
在 product 写入数据时,在分区的 segment 文件中是以追加的形式进行,并且会给每个消息分配唯一的 offset 标识;在 consumer 读数据时,以 offset 作为偏移量,顺序读出数据;
- 现在感觉顺序读写好像理应就应该这样设计,下面给出一组数据对比一下,就知道这种设计是多么的优越!
当磁盘顺序读或写的速度可达 400M/s,而 随机读写的速度只有 几十到几百 K/s,两者差距极大!
了解更多,请关注 公众号 “ code 杂坛 “!
顺序读写可最大程度的利用磁盘的存储特性提速。
零拷贝
介绍零拷贝之前,先介绍两种 Linux 技术,分别是 sendfile、mmap。
sendfile
sendfile :可直接将数据从网卡读至内存[内核空间],反之亦然;交互过程省略应用缓存区[用户空间]的数据暂存。
mmap
mmap:建立磁盘文件与内存的映射关系,内存变更将反射至磁盘[数据内存会暂存,实际落地由系统 flash 时机决定];实现修改内存即变更磁盘。
读写
通过两种技术的结合,可实现 kafka 消息数据读写磁盘的零拷贝,吞吐量较使用传统技术提升 3倍以上。
了解更多,请关注 公众号 “ code 杂坛 “!
在 product 写入数据时,broker 直接从 socket 缓冲区读出数据利用 sendfile 技术写入 内存,而利用 mmap 技术可完成内存与磁盘的状态映射,此刻就已经完成了数据的写入;反之,当 consumer 读数据时,直接把 磁盘数据 读至 网卡[socker]即可。
传统技术
在传统的技术中,磁盘数据的读写,至少需要经历四个过程……
读写
在 读数据 的时候,先把数据从 磁盘 写入 内核缓冲区;再从 内核缓冲区 写入 应用程序缓冲区;再由 应用程序缓冲区 写入 socker 缓冲区;最后由 soccer 缓冲区 写入 网卡缓冲区……反之写数据亦然。
这个时候,你可以回过头看看这个过程。我们只是要“搬运”?份数据,结果却整整搬运了四次。而且这里面,其实都是把同一份数据在内存里面搬运来搬运去,特别没有效率!
了解更多,请关注 公众号 “ code 杂坛 “!
流式处理中扮演的角色
- Kafka 在 监控系统框架 ELK[Elasticsearch + Logstash + Kibana] 中扮演着重要的角色!
- 随着 trace 技术的不断发展、业务数据规模的膨胀,传统的监控框架 ELK [Elasticsearch + Logstash + Kibana] 不足点日渐显露。
其中组件 logstash 由 JRuby 实现,负责日志的收集、加工[规则聚合、过滤…]、数据传输至 elasticsearch。
实现语言的巨大内存消耗、日志加工对节点机器性能的影响、大规模数据中的最终精读……等问题促使了新的组件 filebeat、kafka 的介入。
通过 Filebeat 部署进行日志收集,促使模块解耦,组件部署更加简单且轻量的同时,数据收集更加实时、准确;
通过引入 kakfa 组件实现数据的冗余备份的同时,也支撑了整个框架的横向扩展;更是对数据进行了消峰填谷,提升服务可用性。
所以说,由于 filebeat 、 kafka 组件其出色的设计和特性,对传统监控框架的性能等各方面具有极大的提升。
附录
不要用战术的勤奋来掩盖战略的懒惰
了解更多,请关注 公众号 “ code 杂坛 “!
附:
Filebeat 详细解读
Elasticsearch 原理刨析
什么是云原生 & 市场真正敏捷力量
深度刨析分布式锁 & 详细设计
基于 Consul 实现解析分布式锁
了解更多,请关注 公众号 “ code 杂坛 “!