Apache Kafka 的定位是一个分布式流处理平台,它被设计用于处理高吞吐量的数据流。Kafka 能够在不同的系统和应用程序之间提供实时的数据传输、存储和流处理功能。Kafka 在现代数据架构中扮演着“承上启下”的角色,这意味着它在数据流和处理的不同阶段之间起到了桥梁的作用。具体来说,Kafka 可以从上游系统接收数据,并将其有效地传递到下游系统,同时还能在这些过程中对数据进行加工和处理。Kafka 成为了现代数据架构中不可或缺的消息引擎及流处理平台。
1.主要作用
数据聚合作用
数据采集:Kafka 可以从各种数据源(如数据库、日志文件、传感器、API等)收集数据。
数据整合:它能够整合来自不同源的数据流,形成一个统一的数据平台。
数据缓冲:Kafka 提供了一个缓冲区,可以在数据产生和处理之间平衡负载,防止数据丢失。
数据分发作用
数据分发:Kafka 可以将数据分发到多个消费者,支持多种数据消费模式,如批处理、流处理和实时查询。
数据处理:通过 Kafka Streams 或与其他流处理框架(如 Spark Streaming、Flink)集成,可以对数据进行实时处理和分析。
数据存储:Kafka 提供了持久化存储的能力,可以保留数据一段时间,供后续的消费和处理。
桥接作用
解耦生产者与消费者:Kafka 使得数据的生产者和消费者之间解耦,提高了系统的灵活性和可维护性。
支持异构系统:Kafka 可以连接不同的系统和应用程序,无论是传统的数据仓库还是现代的大数据处理框架。
容错和可扩展性:Kafka 的分布式架构确保了高可用性和可扩展性,即使在部分节点失败的情况下也能保证数据的可靠传输。
2.应用场景
数据管道:在 ETL(Extract, Transform, Load)过程中,Kafka 可以作为数据传输的管道。
事件驱动架构:在微服务架构中,Kafka 可以作为事件总线,支持服务间的异步通信。
实时分析:在需要实时反馈的业务场景中,如推荐系统、欺诈检测等,Kafka 提供实时数据流。
版本:kafka现存1.0.1, 2.1.1, 2.5.0等多个版本
机型:大数据机型+本地磁盘,部署在云厂商ECS;单机价格低,本地盘IO 吞吐较高
副本数量:Topic 默认3副本
保存时间:默认保留3天
SLA:99.9%
Kafka 存储量:4.6PB
成本控制:云服务的费用通常是按使用量计费,随着数据量和流量增加成本会显著上升。自建可避免云服务的隐性成本,例如数据出口费用、额外的存储费用等。
定制化和高可控性:完全的控制权和灵活性,可以根据具体需求定制配置和优化性能。
避免供应商锁定:减少了依赖特定云服务提供商的风险,避免了潜在的供应商锁定问题。
稳定性和可靠性:允许直接管理和维护集群,更快速地响应和解决问题。根据自己的 SLA要求来设计和运维。
性能优化:自建 Kafka 允许针对特定的硬件和网络条件进行性能调优。可以根据业务需求定制监控和报警系统。
尽管自建 Kafka 带来了上述优势,但它也需要维护团队具备相应的技术能力和资源来搭建、维护和升级 Kafka 集群。
起初接入 Kafka 的业务量较小业务类型也简单,写入方、消费方以及维护方享受到了 Kafka 的高性能、高可靠,表面看上去一片祥和。但是在写入、消费的数据流没有统一管控,维护方也没有制定规则,在 Kafka 功能可满足的情况下无节制的满足业务方。
随着业务发展接入 Kafka 的业务量急剧上升,数据流向也越发复杂,隐藏的问题逐渐暴露出来。在数年的累积下,最终表现为 Kafka 集群可用性差、故障时间长、故障频繁、业务影响范围大,Kafka 使用方及维护方苦不堪言。
Kafka 集群磁盘损坏,导致 Topic ISR 频繁切换,造成业务数据积压
ZooKeeper 集群切换 Leader 出现脑裂,触发 Kafka Broker 1.0.1版本bug
Kafka 集群切换 Controller 时间超过 20分钟
同一集群内有不同机房的 Broker,机房间网络发生抖动导致 Topic ISR 频繁切换
业务逻辑异常,Kafka 读写量为预估量 10倍导致 Kafka 性能瓶颈
业务应用调用 Kafka 不做异常捕获,导致 Kafka 单次抖动致使业务崩溃
1.P0工作内容(保障集群可用性)
重保关键业务、精简业务数据调用链路,重点集群更换高性能SSD盘
下线低版本Kafka 集群
制定故障快速止损方案
2.P1通用问题(减少故障次数)
降低掉盘/DOWN机引发 Partition 切换集群压力
减少掉盘/DOWN机引发的集群不可用时长
修盘来带的IO 影响时长
控制单分区过大 Topic 数量
控制数据量过大 Topic 数量
控制 Topic 数据过期时间
虽然面对严峻的线上问题使我们感到压力和不安,但这并不意味着我们无法找到解决的办法。相反,正是这些挑战促使我们深入挖掘问题的根源。我们可以逐步分解问题,识别关键因素,制定切实可行的解决方案。在这个过程中,我们不仅能够提升自己的问题解决能力,还能够增强团队的凝聚力和协作精神。总结为以下几类。
在实际使用中,将不同业务的集群混用会带来一系列的风险和挑战,主要包括:
性能干扰:不同业务性能需求各异,混用致资源竞争,影响整体性能。
隔离性差:混用集群隔离性不佳,业务异常易影响其他业务,难以细粒度控制资源和管理访问权限。
安全性低:不同业务数据安全级别和隐私要求不同,混用致数据泄露风险,难实施严格安全策略和访问控制。
维护复杂:混用增加运维复杂性,需兼顾多业务需求和约束,升级和配置变更协调与风险管理难度大。
资源管理难:混用致资源分配不均,难以预测和规划资源需求,动态调整资源以满足业务变化需求更复杂。
最佳实践是为每种业务场景部署独立的 Kafka 集群,提供更好的性能隔离、故障隔离、维护简便性。
在 Kafka 中,ZooKeeper 扮演着关键的角色,其中最主要的是:
集群元数据管理:ZooKeeper 存储了 Kafka 集群的元数据信息,如 broker 列表、主题和分区的状态、副本信息等。同时Kafka 通过 ZooKeeper 来感知集群成员的变化,例如 broker 的加入和退出。
Leader 选举:Kafka 使用 ZooKeeper 来实现分区的领导者选举。当一个 broker 启动时,它会向 ZooKeeper 注册自己,并参与 Leader 选举。
由于多线上 Kafka 集群共用 ZooKeeper 集群造成 ZooKeeper 压力过大,集群切换 Leader 过程中因数据量较大在 synclimit 时间内未完成同步,出现脑裂。依赖的 Kafka 集群多数 Broker 异常离群,部分 Topic 的 Partition 丢失 Leader,导致严重线上问题。为解决 ZooKeeper 集群压力过大问题,将每个 Kafka 依赖的 ZooKeeper 单独拆分出来。后续新集群部署时统一使用独立 ZooKeeper 集群并做好监控报警,至此此类问题未再出现。
云厂商在同一Region内会有多个可用区供用户选择。由于购买主机时未仔细核对可用区或者是同一可用区目标机型缺货等原因,导致同一集群中部署的 Broker 分布在多个可用区中。不同可用区之间由于存在一定的物理距离,中间经过若干网络设备,因此网络延迟会更大不稳定性增加。
实际遇到的情况是两个可用区之间 Broker 网络抖动导致 Kafka 集群发生 Shrinking ISR。Kafka Broker 在 ISR 发生 Shrinking ISR 时需要申请 leaderIsrUpdateLock 写锁。在 ISR 集合发生变更时对高水位线的的更新需要加写锁,此时会与消息生产、消息消费、Partition 副本消息复制锁竞争,导致 Kafka 并发度急剧下降,影响读写 Topic 消息。
因此,此后规定部署 Kafka 集群需要使用同一可用区、同一机型主机。
最初部署 Kafka 时社区稳定版本较低(1.0.1),随着时间推移不断有新 Bug 被暴露出来。经常有莫名其妙的线上问题反馈,好在有活跃的社区分享能解决绝大部分问题。随着 Kafka 版本的不断更新,Bug也越来越少、服务越来越稳定。目前低版本集群统一升级到了内部使用较为稳定版本(2.5.0)。
起初部署 Kafka 集群为手动部署,每次部署流程繁杂。操作同学极为痛苦、易错且动辄小时级别的交付时间在业务角度是不可接受的。部署集群的逻辑很简单,购买初始化机器后将 Kafka 二进制包同步到目标机器并生成配置文件,启动服务即可。自动化后可分钟级交付集群。
历史上对于新建 Kafka 集群所需底层资源没有统一的标准,分配过多导致资源浪费,分配过少无法承载业务流量,影响集群性能。同时繁杂的副本均衡操作会引起大量的 I/O,进而降低集群读写性能,影响线上业务。
Kafka 核心功能涉及数据的持续读写,可以被认为是一个 I/O 密集型的系统。这意味着 Kafka 的性能和效率很大程度上依赖于底层的 I/O 子系统,包括磁盘、网络和文件系统等。
经分析,将 Kafka 主要依赖资源项目(I/O、存储量)作为核心评估点,抽象成计算公式用于申请底层资源。
在 Kafka 中,可靠性和可用性是两个非常重要的概念,它们之间往往需要进行权衡和平衡。
可靠性指的是 Kafka 确保消息不丢失的能力。为了提高可靠性,Kafka 提供了以下几个关键特性:
副本机制:在多个 broker 上保存消息的副本,即使某个 broker 宕机,消息也不会丢失。
ACK 机制:生产者可以设置 ACK 级别,确保消息被正确写入到所有同步副本(ISR)后才认为是成功。
持久化存储:Kafka 将消息持久化到磁盘上,确保即使在 broker 重启后消息仍然可用。
可用性是指 Kafka 集群能够在任何时间点提供服务的能力。为了提高可用性,Kafka 采取了以下措施:
分区机制:通过将主题划分为多个分区,可以在部分 broker 宕机时仍能提供服务。
领导者选举:当 leader 副本所在的 broker 宕机时,会自动从 ISR 中选举新的 leader,以保证服务的连续性。
容错机制:通过 ZooKeeper 协调,Kafka 能够检测到 broker 的宕机并及时进行故障转移。
在实际应用中,我们通过以下两项关键配置在可靠性与可用性之间找到一个平衡点:
ACK 级别:对于核心业务(如:IM、支付等)设置较高级别确认方式。对于非核心及并发有较高要求的业务, leader 副本确认即可。
Unclean Leader Election:对于核心业务(如:IM、支付等)关闭此配置,确保集群可靠性,并将集群迁移至高性能资源上。对于非核心业务使用集群开启此配置,允许非 ISR 中的副本成为 leader,确保集群可用性。
老版本 delete-topic command 中使用连接 ZooKeeper 进行 Topic 删除操作,但是由于 Broker 未开启 delete.topic.enable,ZooKeeper 会标记删除并不会真正删除。随着业务迭代,不断有 Topic 创建、删除,导致大量脏数据存留在 ZooKeeper 中。
较多的脏数据留存在 ZooKeeper 中可能会导致 Partition Leader 选举失败、切换 Controller 持续时间长、reassign 异常等问题。
起初针对 Kafka 只有 Broker 存活以及 ISR 可用数量监控,当报警来袭故障来临那一刻,对于维护人员来说是茫然的。后期我们针对 Kafka 内部运行以及调用 Kafka 相关的监控大盘,其中主要包括:
Kafka 依赖的 ZooKeeper 延时、各种连接状态
Broker 总览 / 不同步 Partition 数
Broker JVM 各种信息
Broker Producer、Consumer 延时正态分布
Broker Producer、Consumer 请求量
Broker 均衡情况
Topic 读写量、数据量、分区数据量
Topic 消息积压数量
Kafka使用机械磁盘由于长时间承载大量 I/O 请求,磁盘不定期损坏。针对磁盘额外增加两项监控:
磁盘健康程度
综合磁盘 I/O 使用率、读写速度对磁盘读写性能进行监控,磁盘读写速度低于设置阈值 I/O 使用率却超过设置阈值时,表示磁盘性能已不满足要求。磁盘读写性能下降时可能触发 Shrinking ISR,进而大幅影响集群并发度。
异常磁盘占比
Kafka 集群主机包含多块机械磁盘,当磁盘损坏不可读写时会触发Partition Leader 切换,切换后暂时丢弃本磁盘。当主机磁盘连续损坏多块时,不仅会导致剩余磁盘分担全部 I/O,还会导致ISR副本数量过低造成数据丢失风险。因此,需要增加机器异常磁盘数量占比做出监控报警,及时替换故障主机。
在 Kafka 中,负载不均和热点数据是两个可能影响集群性能和稳定性的因素。负载不均是指某些 broker 或分区比其他部分承受更多的负载。这可能导致某些节点过载,而其他节点则相对空闲。
负载不均可能由以下原因造成:
不均匀的分区分配:分区分配不均匀,某些 broker 可能会承载更多的分区,从而导致负载不均。
消费者组不平衡:消费者组内的消费者数量不均衡,或某些消费者的处理能力较弱,导致某些分区被过度消费。
生产者流量不均:生产者发送的消息集中在少数几个主题或分区上,可能会导致这些分区成为热点。
热点数据是指那些被频繁访问或更新的数据。在 Kafka 中,如果某个主题或分区接收到的消息量远大于其他主题或分区,它就成为了热点。
热点数据可能导致以下问题:
网络拥塞:热点数据可能导致网络带宽被大量消耗,从而影响其他正常流量。
磁盘 I/O 压力:热点数据可能导致磁盘 I/O 压力增大,影响磁盘性能。
延迟增加:热点数据可能导致消息处理的延迟增加,影响消费者效率。
定位具体 Topic、Partition 后可以采取以下措施:
扩容分区数量:对于写入量增多分区数量较少的 Topic,可增加分区数来均衡负载。
重新分配分区:通过 Kafka 的分区重分配工具,可以手动或自动调整分区的分布,以实现更均匀的负载。
调整消费者组:确保消费者组内的消费者数量平衡,并根据消费者的处理能力合理分配分区。
使用自定义分区器:生产者端使用自定义分区器,可将消息均匀地分配到不同的分区,避免热点的产生。
在 Kafka 中,当分区的 Leader 副本不可用时,分区的领导者状态可能会被设置为 -1
。这通常表示该分区目前没有可用的 Leader 副本,因此无法对该分区进行读写操作。此种为严重事故需要及时介入处理回复 Kafka 可用性,监控报警是必不可少的,需要在业务报警反馈之前介入处理。
Kafka 作为消息中间件写入、消费的业务程序众多,其中涉及的业务逻辑及调用 Kafka 的平台、形式各不相同。业务出现问题时,多数业务都无法直接定位是 Kafka 服务端出问题还是调用 Kafka 的客户端出问题,排查时间长。为提升故障排查效率,我们上线了 Kafka 集群读写基线。通过真实的建连对 Kafka Topic 进行写入、消费消息,计算所需延时。当业务出现问题时可以直接查看对应 Kafka 集群读写基线,快速确定排查方向。
由于 Kafka 长时间处于“野蛮生长”状态,无任何标准约束,对消息读写无任何限制,致使 Kafka 运行在崩溃边缘。因此我们参考公有云消息中间件产品规则上线了《Kafka服务等级协议(SLA)》。其中包括对业务提供 Kafka 服务的可用性承诺以及对使用的一些限制。
限制项 | 限制值 | 说明 |
---|---|---|
跨环境调用 | 不支持 | 生产、测试环境不可互相调用。 |
版本升级 | 不支持 | 实例创建后,服务端版本不支持升级。 |
自动创建Topic | 不支持 | 需要通过运维工单申请。 |
Topic负责人 | 写入方 | 运维工单申请创建时记录,写入、消费产生的负载及影响由Topic负责人负责。 |
Topic存储容量总和 | 20 TB | Topic整体容量<=20 TB。飞书群持续巡检推送不符合规范的Topic。 |
单分区存储容量 | 20 GB | Topic单分区最大容量<=20 GB。飞书群会持续巡检推送不符合规范的Topic。 |
分区数 | 512 | Topic最大分区数不得超过512。 |
使用量波动 | 3倍 | 读写总量增加至原来的3倍需要提前一个工作日通知。 |
Topic | 不可以包含特殊字符 | |
消费组名称 | 不可以包含特殊字符 |
其中的一些限制项目的来源也大部分来自于以下不规范使用问题。
Kafka 定位为分布式消息引擎,并非是数据库更不是存储系统。多数业务将大量、长时间消息留存在 Kafka 中作为其系统的数据灾备工具。Topic 数据过大对 Kafka 集群来说有诸多潜在的负面影响,以下列出了一些主要的弊端:
重新平衡时间延长:当 Kafka 发生再平衡(如一个新的消费者加入消费者组,或者 broker 失败)时,较大的分区数据会导致再平衡时间显著增长,这会影响整个集群的稳定性和可用性。
性能瓶颈:单个消费者每次只能从一个分区拉取数据,如果该分区非常大,可能导致处理能力不足或延迟增加。
内存压力:broker需要在内存中保留一部分索引信息来维护数据的快速查找。索引文件越大,占用的内存就越多,同时增加 Java 垃圾回收的压力。
文件句柄限制:分区由多个文件组成,一个分区中的每个日志段(segment)都需要独立的文件句柄。如果单个分区过于庞大,可能会消耗大量文件句柄,给操作系统带来压力。
数据热点:分区数据过大可能导致“热点”问题,尤其是当处理这些分区的 broker 没有足够资源,比如 CPU、磁盘 I/O 或网络带宽时。
长期存储和维护问题:随着时间推移,Kafka 分区过大会导致长期的数据存储和管理变得困难,比如压缩、清理过期消息(log compaction、log retention)等会变得效率低下。
故障恢复风险:如broker异常,恢复和重新同步数据会花费更多时间。在数据过大的情况下,恢复故障分区时候对于剩余健康的分区造成额外的负担,容易导致系统整体压力增大,风险扩散。
基于以上信息我们在 SLA 中要求业务方将 Topic 分区总量控制在 20TB以下、单分区数据量控制在 20GB以下,并协助业务方进行修改。降低 Kafka 最小数据单元的数据量,使 Kafka 更加轻量化。
部分业务由于初始估计不足、缺乏经验以及历史因素等原因将 Topic 分区数设置的不合理。
Topic分区设置过少,可能会导致以下几个问题:
吞吐量限制:Kafka 的吞吐量是由分区数量决定的。每个分区都可以独立地处理读写操作,因此分区越多,理论上吞吐量越高。如果分区设置过少,可能会限制 Kafka 的吞吐量。
负载不均:如果分区设置过少,所有的读写操作都会集中在少量的分区上,这可能会导致某些 broker 过载,而其他 broker 则相对空闲,从而造成负载不均。
热点问题:如果分区设置过少,可能会出现热点问题,即某些分区接收到的消息量远大于其他分区,这可能会导致这些分区成为性能瓶颈。
数据倾斜:如果分区设置过少,可能会导致数据倾斜,即某些分区的数据量远大于其他分区,这可能会影响数据的均衡性和查询性能。
Topic分区设置过多,可能会导致以下几个问题:
集群元数据增加:当分区数量增加时,Kafka 集群需要维护更多的元数据,这可能会增加 Zookeeper 的负载,影响其稳定性和性能。
控制器压力增大:Kafka 集群中有一个 broker 角色称为 “控制器” (controller),它负责管理分区领导者和副本状态的更改。每个分区和副本的更改,都需要控制器介入,过多的分区可能会导致控制器的负载急剧上升。
leader选举开销:发生 broker 故障时,Kafka 需要为故障节点上的分区选举新的leader。如果有大量分区需要选举,这个过程可能会变得缓慢,影响整个集群的可用性。
跨 broker 流量增加:分区和副本的数量会影响集群间的重新平衡(rebalancing)和数据复制。如果有过多的分区,这可能会产生大量的网络流量,导致网络拥塞和延迟。
资源开销增大:每个分区都会消耗一定数量的文件句柄、内存和 CPU 资源。在 broker 上运行的分区数量过多,可能会超出磁盘 I/O、网络带宽或其他系统资源的限制。分区数太多,会导致存储碎片化严重,集群性能和稳定性都会急剧下降。
客户端性能问题:生产者和消费者在启动及运行时都需要加载所有分区的状态信息,当分区数过多时,会增加客户端初始化的时间和内存消耗。
为了避免这些问题,在创建 Topic 时根据预期的吞吐量、并发度和数据量来合理设置分区数量。
通过查看 Topic 相关监控,经过多年的“积累”其中有近 50% Topic属于不活跃 Topic。产生这种现象的原因大致可归纳为以下几点:
申请 Topic 后未使用
错误申请
业务下线后未清理 Topic
只生产,无消费
这种不活跃的 Topic 带来的危害如下:
资源占用: 每个 topic 和 partition 都会占用一定的资源,包括内存、文件句柄、以及与之相关的数据结构等。即使 topic 是空的,Kafka 的 broker 也需要为其分配这些资源,并将其信息保存在 Zookeeper 中。随着空 topic 的数量增加,这些资源的总占用将会变得显著,从而可能影响到其他有实际数据负载的 topic 的性能。
管理复杂性: 有大量的空 topic 可能会导致集群的管理变得更加复杂,使得维护人员难以识别哪些 topic 是有用的,哪些是无用的。这可以增加错误的可能性,比如误删除有价值的 topic。
元数据负载: Kafka 集群会维护所有 topic 的元数据信息。大量的空 topic 意味着有更多的元数据需要被维护和同步,这可能导致控制器的负载增加,影响整个集群的元数据操作性能。
启动时间: 当 Kafka 集群重启时,它需要加载和验证所有 topic 的状态。如果存在大量的空 topic,这个过程可能会导致启动时间延长。
Leader选举: Kafka 中的每个 partition 都会有一个 leader副本来处理读写请求。即使是空的topic,也需要进行 leader 选举。如果有大量的空 topic 和 partition,这将导致不必要的 leader 选举开销。
Zookeeper 负载: Kafka 使用 Zookeeper 来维护集群的元数据和状态信息。大量的空 topic 相关信息存储于 Zookeeper 可能会给 Zookeeper 带来额外的负载和存储需求,影响其性能和稳定性。
依赖完善的监控系统采集 Kafka 相关指标,通过对 Kafka 进行多维度巡检反向推动业务方改造,以上各种问题基本治理完毕。
互联网所有系统都不能保证 100% 可用性,Kafka 也不例外。除了服务自身的稳定性会出现问题、网络等基础设置也存在稳定性问题。因此在不影响业务逻辑的前提下控制好生产、消费 Kafka 的重试间隔、重试次数可以有效减少消息丢失。
无论是生产者还是消费者,都需要妥善处理可能出现的异常。如果在代码中没有适当地捕获和处理异常,可能会导致消息丢失、重复甚至导致进程退出等严重后果。异常捕获属于程序健壮性问题,不仅调用 Kafka 需要增加异常捕获,任何可能会中断程序运行的异常都需要增加异常捕获。
在 Kafka中,subscribe和assign是消费者API提供的两种不同的方法,用于指定消费者应该消费哪些主题或分区。这两种方法的主要区别在于它们如何影响消费者组的分区分配和再平衡行为。
assign 模式下,消费者明确指定要消费的分区。这种方式允许消费者精确控制哪些分区由哪些消费者实例来处理,非常适合于需要精细控制资源分配和管理的情况。
subscribe 模式更为灵活和动态。消费者可以注册一个或多个主题,Kafka 会自动为其分配分区。这种方式适合于消费者只需要关注主题而无需关心具体分区的情况,或者消费者希望Kafka自动平衡分区负载的情况。subscribe模式也支持消费者动态地加入或离开主题,这使得它非常适合于消费者需求变化较大的应用场景。
值得注意的是,assign和subscribe模式不能混合使用。如果一个消费者组内既有使用 assign 模式的消费者又有使用subscribe 模式的消费者,那么Kafka将无法正确地为这些消费者分配分区,可能导致数据丢失或重复消费等问题。
少部分业务使用 assign 消费模式的框架(如:Flink)完成业务逻辑,但未知晓 assign 消费模式的含义,变更 Topic 分区数导致消息丢失。
当一个Kafka Topic 的消费者数量过多时,可能会出现以下几种危害:
网络拥塞:每个消费者都需要从 Kafka 集群中读取消息并进行处理,这将占用大量的网络带宽。如果消费者数量过多,可能会导致网络拥塞,进而影响消息的传输效率和系统的整体性能。
资源竞争:消费者数量的增加意味着更多的进程或线程需要访问 Kafka 服务器,这可能导致 CPU、内存和其他计算资源的竞争加剧,从而降低系统的处理能力。
消费者组管理开销:每个消费者都属于一个消费者组,Kafka 需要管理这些消费者组的状态,包括分区的分配和再平衡。消费者数量过多会增加 Kafka 集群的管理开销,尤其是在消费者组动态变化时。
消费者实例之间的协调开销:在消费者组中,消费者实例之间需要进行协调以实现分区的均匀分配。消费者数量过多会增加这种协调的开销,尤其是在分区重新分配时。
延迟增加:随着消费者数量的增加,每个消费者可能分配到更少的分区,导致单个消费者的吞吐量降低。此外,消费者数量的增加也可能导致 Kafka 集群的处理延迟增加。
稳定性下降:过多的消费者可能导致 Kafka 集群的稳定性下降,因为 Kafka 需要处理更多的客户端请求,这可能会影响到 Kafka 集群的正常运行。
产生这种现象的原因主要是业务方对数据流架构设计无管控、无合理规划。
频繁收到某业务 Kafka 集群主机 I/O 报警,也有部分反馈读写 Kafka 不稳定。经排查,发现集群内存在部分脉冲式消费者,即在短时间内集中处理大量数据。
脉冲式消费主要危害:
系统压力过大:在短时间内处理大量数据会对系统造成巨大的压力,可能导致系统性能下降,甚至出现服务不可用的情况。
资源瓶颈:数据库、网络带宽、计算资源等在脉冲式消费期间可能会达到瓶颈,影响系统的稳定性和响应速度。
服务质量下降:在高负载下,服务的响应时间可能会变长,用户体验和服务质量可能会受到影响。
数据一致性问题:在处理大量数据时,可能会出现数据不一致的问题,尤其是在分布式系统中。
错误率上升:系统在高负载下更容易出错,可能会导致数据处理错误或丢失。
成本增加:为了满足脉冲式消费的需求,可能需要临时增加硬件资源,从而增加成本。
通过与业务方沟通,优化消费逻辑。减少消费数据量、分散消费时间点后问题得到解决。
Kafka 与其他开源组件一样由于设计理念、设计架构的原因导致一些痛点问题无法根治,这些痛点问题作为 Kafka 的“先天问题”会在特定场景下影响其性能或可用性。在不进行架构重构或更换其他组件前提下无法改变,因此我们采取的措施是避免或减少此类问题的发生。
当 Kafka 集群负载到达瓶颈需要扩容时,新加入集群的 Broker 需要经过数据迁移才能分担集群读写压力。但是数据迁移过程中需要从原有 Leader 副本节点拉取数据,这一过程会增加 Leader 副本所在 Broker 的I/O压力,导致集群性能进一步下降。这是 Kafka “存算一体”的设计理念所导致的,我们通过以下几个方法来规避此类问题的出现频率:
提高 Kafka 集群资源冗余度
在准确预估 Kafka 用量的前提下,增加资源配置冗余度。这样虽然会增加成本消耗,但是可增加对突发请求的支撑。
降低资源瓶颈预警水位,提前扩容
将集群水位报警阈值调低,提前制定好低峰期扩容计划。
临时降低数据量进行扩容操作
在扩容集群前对数据量较大的 Topic 的保存时间进行缩短以减少单分区数据量降低 I/O 消耗、减少对集群性能影响、加快扩容速度。
要求业务控制数据量大小
《Kafka服务等级协议(SLA)》中规定业务使用 Topic 总体数据量以及单分区数据量大小,目的是降低最小单元内的数据量大小,以保证 Kafka 的轻量性,在故障恢复、集群扩缩容期间花费更少的时间。
为保证 Kafka 高可用、实现故障转移,通常 Topic 会设置多个副本。Leader 副本负责提供数据读写,Follower 副本只做热备用途。Follower 副本从 Leader 副本中拉取分区数据。原生 Kafka 只可配置副本复制的线程数无法控制副本复制的速率,因此在 Broker 重启后其所有副本全部切换为 Follower副本,大量的副本所需拉取的数据量非常可观。大量的I/O需求作用到其他 Leader 副本所在的 Broker 上,会对集群整体性能、可用性产生巨大影响。
我们对于此种问题采取的规避措施是,主动运维中减少重启 Broker 的操作。在更换异常数据盘方面借鉴了批量写理念,在损坏单一数据盘时只做强制 Leader 副本切换操作,当某 Broker 损坏数据盘数量达到阈值时进行一次全量更换数据盘操作,重启 Broker。
消费者组重平衡的主要危害会降低 Consumer 消费消息的速度,对业务产生负面影响。触发消费者组重平衡的条件如下:
Topic 分区发生变化(主动运维、预期内)
Consumer 数量发生变化
影响消费者组重平衡时间的因素为消费者的数量。
基于以上情况,我们采取的规避措施:
限制 Topic 分区数量间接限制消费者数量以减少重平衡时间
调整 Consumer 端参数如:session.timeout.ms、heartbeat.interval.ms、max.poll.interval.ms
经过一段时间的系统性治理,Kafka 目前已趋于稳定,近一个季度无故障。不过,仍有诸多不足亟待解决:
无统一接入:接入方式多种多样无统一的SDK进行管控。
成本高昂:Kafka 主要性能瓶颈在 I/O,其对应的 CPU、内存存在大量浪费。
扩展效率低:计算节点与存储节点强制绑定,无法快速平稳扩容。
分层存储、存算分离快速扩展等方向将是我们后续主要探索方向。Pulsar 作为下一代云原生分布式消息流平台,其宣称的高吞吐、低延迟、存算分离架构与我们的需求十分契合。
因此,初步完成了对 Pulsar 的可行性调研,调研报告待续。