首页 / 资讯中心 / 文章详情

Kafka压测实战:三台廉价服务器如何跑出200万TPS

Kafka压测实战:三台廉价服务器如何跑出200万TPS ★ FEATURED ARTICLE
去年年初接手一个急活儿给一套Kafka集群做压测目标是200万TPS写峰值。消息传开后团队里讨论得很热烈共识是“这个量级起码得上高配机器”。可预算就那么多最后只凑出三台中低配的旧服务器连NVMe都没有清一色SATA机械盘。我当时心里也在打鼓但等压测完成后成绩单却出乎所有人预料这三台“廉价”服务器不仅跑到了200万TPS还稳定维持了几十分钟。这篇内容就是那次压测的完整复盘。目标怎么拆、机器怎么选、工具怎么用、参数怎么调以及在压测里一步步撞见的坑我都会摊开讲。对正在做Kafka容量评估或者被“压测就是上不去”困扰的同学这篇应该能帮你省下不少弯路。1. 压测目标拆解200万TPS背后是三笔账1.1 先弄清楚要压的是哪一段链路当时的需求来源是一个即时通信平台项目的容量评估业务高峰时段在线状态更新、消息投递和日志采集会产生很大的写入压力。200万TPS不是拍脑袋想出来的指标而是根据单机写入速率、用户规模、消息量总和倒推出来的。收到需求后第一件事不是打开压测工具而是和业务方一起把“压什么”定义清楚。Kafka的压测一般分三种口径Producer → Broker生产链路压的是写入能力Broker → Consumer消费链路压的是读取能力Producer → Broker → Consumer端到端压的是整条链路同时跑满的能力。这次容量评估的重点是生产链路因为高峰时刻最先承受压力的是写入方向。消费能力我做了单独验证但不会让读写同时把网络打满否则你就分不清瓶颈到底在哪一段。1.2 TPS和流量之间有一个容易被忽略的换算200万TPS听上去很吓人但实际能不能做到很大程度上取决于单条消息有多大。TPS是按消息条数算的而网络、磁盘真正消耗的是字节数。这中间差着一个“消息大小”的放大系数。按常规的业务消息大小算了一笔账单条消息大小每秒数据量对应带宽100字节200 MB/s1.6 Gbps400字节800 MB/s6.4 Gbps1024字节2000 MB/s16 Gbps我们这次压测用的消息大小是400字节左右所以200万TPS对应的原始数据量是800MB/s。如果是1KB的消息同样200万TPS单靠3台服务器加万兆网卡就不一定吃得消了。任何压测开始前第一件事就是把消息大小分布摸清楚否则后面所有数字都是空谈。1.3 明确上限类型是单节点上限还是集群系统上限三台broker要撑200万TPS意味着平均每台机器要扛大约66万条/秒的写入。这个数字单看并不夸张Kafka的架构设计本身就是为了高吞吐但它对参数配置非常敏感。我在动手之前把目标拆成了四个资源维度网络网卡吞吐和包速率、磁盘顺序写速度、CPU协议解析、压缩解压、内存拷贝、内存页缓存是否够用。每一笔账都先算清楚再决定硬件怎么选、参数怎么调。压测最忌讳的情况就是目标都还没拆明白就直接上工具开跑最后得到一个没法解释的结果。2. 三台“廉价”服务器的选型逻辑预算到底该花在哪2.1 最终硬件配置单所谓“廉价”不是随口说的。这批机器基本都是机房淘汰级别的旧设备CPU型号老、内存频率低、没有独立RAID卡更没有什么NVMe加速盘。真正算得上“特意选了配置”的地方只有万兆网卡和机械盘数量。角色CPU内存磁盘网卡Broker 1-3双路老款E5共28核64GB DDR3/DDR44×2TB SATA HDDJBOD万兆光口压测机 ×48核16GB任意SSD万兆或双千兆bond这里必须说明一下压测机如果网卡不够可以用两个千兆口bond起来凑带宽但更推荐直接上万兆。因为压测机自己如果先撑不住broker的CPU还没出汗TPS就已经上不去了你还会误以为瓶颈在broker那边。2.2 磁盘可以“便宜”但数量不能少Kafka最核心的存储设计就是顺序追加写。每条消息进来只是往分区日志的末尾追加一条记录读的时候优先走页缓存。这样的IO模型对机械盘非常友好——机械盘最怕随机IO但顺序写恰恰是它的强项。单块普通SATA盘顺序写也能跑到150-200MB/s这个数字在Kafka场景下完全够用。我们这次的账是这样算的消息400字节开了lz4压缩之后落盘大约200字节200万TPS的写盘总量在400MB/s左右。四块机械盘做JBOD没有RAID卡就直接直通理论顺序写600-800MB/s实际扣掉文件系统开销也能稳定在500MB/s以上等于留了二到四成的余量。这就是为什么可以用机械盘Kafka把随机IO变成了顺序IO机械盘在这方面并不吃亏。相比SSD机械盘的单位容量成本低得多而且大容量盘做日志存储也更合适。唯一要注意的是盘的数量不能太少单盘扛400MB/s肯定不够至少四块起步。2.3 网卡和内存是真正不能省的地方预算优先级我最后定了这么一条网卡 内存 CPU 磁盘数量。网卡的计算很简单。未压缩的400字节消息200万TPS就是800MB/s开了压缩之后实际网卡流量大约在400MB/s。万兆网卡的有效带宽大约1100-1250MB/s所以单向写入完全扛得住。但如果你把replication factor开到3broker之间复制流量直接翻倍万兆就会顶到天花板。所以这次极限压测用的副本数是1生产环境如果要开多副本是需要按另一个口径另外评估的。内存方面Kafka的Broker内存主要不是给JVM用的而是给操作系统页缓存。消费的时候数据直接从页缓存里读能命中内存就完全不用碰磁盘读吞吐可以高出好几个数量级。64GB内存刨去JVM堆Kafka官方其实不建议堆内存设太大4-6GB就够剩下的几十GB全是页缓存的地盘。swap必须关掉否则内核在内存紧张时把进程换到磁盘上吞吐会直接崩掉。注意好多人压测翻车不是因为磁盘慢而是网卡满载导致TCP重传飙升producer端像堵车一样排队。所以预算有限时宁可少上一块盘也要保证网卡是万兆。3. 压测工具与测试拓扑官方脚本打底多机并发加压3.1 官方工具已经够用不要一上来就堆框架压测Kafka没必要一上来就上复杂的可视化压测平台Kafka发行包自带的kafka-producer-perf-test.sh和kafka-consumer-perf-test.sh已经能覆盖大部分场景。它们能直接打印TPS、平均延迟和99分位延迟最关键的是支持--print-metrics可以导出client端和broker端的JMX指标对定位瓶颈非常有帮助。我当时的生产压测命令长这样bin/kafka-producer-perf-test.sh \ --topic perf-topic \ --num-records 20000000 \ --record-size 400 \ --throughput -1 \ --num-threads 8 \ --producer-props \ bootstrap.serversbroker1:9092,broker2:9092,broker3:9092 \ acks1 \ batch.size1048576 \ linger.ms20 \ compression.typelz4 \ buffer.memory268435456 \ max.in.flight.requests.per.connection5 \ --print-metrics几个参数简单解释一下--throughput -1表示不限速测试最大能力--record-size 400和业务消息大小对齐--num-threads 8是让单台压测机开8个生产者线程模拟并发--producer-props后面的所有配置都会传给生产者客户端。消费端验证用另一个命令bin/kafka-consumer-perf-test.sh \ --broker-list broker1:9092,broker2:9092,broker3:9092 \ --topic perf-topic \ --messages 20000000 \ --threads 8 \ --print-metrics因为重点是生产链路消费端我只用来确认“能消费且消费速度跟得上”不会和生产者同时压满网络。3.2 为什么需要4台压测机同时打单台压测机跑不满目标。我们那台8核压测机开8个生产者线程极限大概在40-60万TPS。想到200万就必须横向扩展压测机。实际拓扑是4台压测机每台负责大约50万TPS的量级各自跑独立的压测进程共享同一个topic。我当时的启动逻辑写了个简单的shell脚本在4台机器上同时拉起压测任务for host in perf01 perf02 perf03 perf04; do ssh $host cd /opt/kafka/ nohup bin/kafka-producer-perf-test.sh ... /tmp/kafka-perf-$(hostname).log 21 done压测机建议和broker放在同一个内网网段跨交换机跨专线之后延迟一抖动结果就不纯粹了。多机压测时记得把日志统一汇总到一台机器用grep抓每个时间点的TPS汇总。3.3 压测过程中要盯的实时指标压测不是启动完就干等我习惯开两个终端一个盯broker一个盯压测机。broker一侧需要看的内容sar -n DEV 1网卡吞吐、丢包率和重传情况iostat -x 1磁盘写吞吐、IO队列长度、awaitvmstat 1CPU、内存、swap的变化。压测机一侧重点看topCPU是否打满有没有GC线程频繁出现ss -sTCP连接状态有没有大量TIME_WAIT如果TPS上不去先怀疑压测机自己再到broker上排查。这些命令看着基础但比任何“高端压测平台”都直接。4. 参数调优全景图从Producer到Broker再到OS一层都不能少4.1 Producer端参数batch和linger是天生一对刚开始压测时几乎所有默认配置都是“能用就行”但Kafka的生产者参数对吞吐的影响是决定性的。核心就是batch.size和linger.ms这一对组合。batch.size默认16KB我调到了1MB。生产者会把发往同一分区的消息攒成一个批次再发送批次越大单次网络请求携带的有效数据越多吞吐自然也越高。linger.ms默认0意思是攒够就立即发送。我调到20ms意思是“最多等20毫秒再发”。这样每个线程每秒能发出几十个批次配合大batch吞吐提升非常明显。很多人担心linger调大会增加延迟但20ms对大多数非实时业务完全可以接受换来的是吞吐量成倍提升。这是一个典型的“用小延迟换大吞吐”的取舍。另一个必须调的是compression.type。我用的是lz4400字节的JSON消息大约能压到一半网络和磁盘压力立刻减半。zstd压缩率更高但CPU开销略大snappy居中。压测看极限吞吐的时候如果CPU有富余用zstd压得更多反而更利好吞吐。acks的选择也很关键acks0最快但会丢消息acks1是leader写本地日志后返回acksall要等所有副本确认。为了逼近极限又不是完全失真我选了acks1。容量评估场景下这个选择是合理的但如果你测的是可靠性指标那就得用acksall两种测试口径不要混着谈。其他生产端参数也顺手调了buffer.memory从默认的64MB调到256MB避免发送缓冲拥塞max.in.flight.requests.per.connection保持5让请求管道更饱满retries压测场景直接设0否则重试会掩盖真实性能问题。4.2 Broker端参数让网络线程和IO线程先吃饱broker端有几个默认参数偏保守需要调大。我并不建议把每个参数都调到最大而是要让线程数和机器实际负载匹配。参数默认值压测推荐值调整理由num.network.threads39网络线程至少要和网卡队列数匹配num.io.threads816磁盘IO和请求分发的线程数写高峰时很容易打满socket.send.buffer.bytes1024001048576调大TCP发送缓冲减少小包socket.receive.buffer.bytes1024001048576配合上面一条使用queued.max.requests5001000网络线程与IO线程之间的排队伍深度background.threads1016管理日志段清理和索引更新的后台线程分区数的设计也要单独说。我用的是“并发线程数≤分区数”的原则4台压测机×8线程32个并发发送任务topic分区数至少32我最终用了48个分区。分区太少会导致压力全部堆积在某一个broker的分区上哪怕其他broker闲着也没用。副本数这里必须实话实说极限压测时我把replication factor设成了1。如果开3副本broker间复制流量会让万兆网卡接近饱和200万TPS就很难成立。生产环境用多副本换可靠性是另一回事但压测“峰值能力”和“可靠性配置”是两个口径不要混在一起评估。4.3 操作系统这层最容易被人漏掉broker跑在Linux上OS参数不调应用层再努力也白搭。我这次专门花了半天时间处理OS层关swap把vm.swappiness0写入sysctl配置防止内核把进程换到磁盘文件句柄ulimit -n 65535Kafka分区分多了会打开大量文件句柄网卡多队列用ethtool -l eth0查看当前队列数量如果支持就ethtool -L eth0 combined 16把网卡中断分散到多个CPU上中断合并网卡驱动支持时打开adaptive-rx减少小包造成的中断风暴TCP缓冲区适当调大net.core.wmem_max和net.core.rmem_max配合broker的socket buffer参数一起生效。页缓存是Kafka读性能的关键它的作用比JVM堆内存大得多。Kafka自己不缓存消息数据完全依赖操作系统页缓存。所以内存要大、swap要关消费流量最好全部命中页缓存磁盘在消费场景下几乎不出场。这个优势在端到端测试时尤其明显也是我敢在3台机械盘机器上跑200万TPS的底气所在。忠告每次只动一组参数记录改动前的TPS、延迟和负载指标再进入下一轮。不要一次改10个参数出了问题你根本不知道是哪一项起了反效果。5. 三轮压测结果复盘从70万TPS到200万TPS的两次跳跃5.1 第一轮只用默认配置结果只有70万TPS第一轮几乎是用默认配置开场batch默认16KB、linger默认0、没开压缩。结果很明显TPS在60万到70万之间晃动。看监控数据broker网卡入向流量约600MB/s离万兆上限还有一段距离磁盘写吞吐只有200MB/s左右磁盘根本没吃饱三个broker的CPU利用率都在20%-30%徘徊。这个状态是典型的“批次太小、协议开销大”。大量小请求在网络线程上排队CPU虽然不高但都在做无谓的解析和组装。第一轮只调了Producer端的三个参数batch.size调到1MB、linger.ms调到20、开启lz4压缩结果很快直接蹦到120万TPS。5.2 第二轮120万TPS之后开始撞磁盘120万TPS维持了一段时间但开始出现回落抖动。这时候看iostat磁盘写吞吐在500MB/s上下而四块机械盘JBOD的实际稳定上限也就600MB/s左右已经逼近极限。与此同时其中一个broker的num.io.threads线程池使用率明显偏高。这轮把broker的num.io.threads从8调到16再把log.segment.bytes从1GB调整到512MB减少单个日志段文件在后台清理和恢复时的开销。磁盘没有再明显掉速TPS从120万升到150万左右但网卡流量又开始抬头。5.3 第三轮最终200万TPS是怎么跑出来的真正推到200万的关键动作有三个分区数从16增加到48把并发压力均匀摊到所有broker上压测机从2台增加到4台单台压测机的8线程不再吃紧acks1保持不变但确认buffer.memory没有成为瓶颈max.in.flight.requests.per.connection保持在5。最终结果稳定在200万TPS压测轮次TPS峰值主要瓶颈关键变化第1轮约70万小包无压缩调Producer的batch/linger/压缩第2轮约120万磁盘写接近上限调Broker的IO线程和日志段大小第3轮约200万无明显瓶颈余量10-15%加分区加压测机客户端参数最后一轮我让4台压测机同时跑10分钟每台机器日志都在50万TPS上下合计200万而broker三台机器网卡入向流量约400MB/s压缩后磁盘写约380MB/sCPU利用率在45%-60%。这说明系统还有一定余量再往下压一点还能往上走但那个数字已经不是容量评估关心的范围了。5.4 关于消费链路的一次补充验证生产链路验证通过后我单独把消费压测跑了一轮。因为消费有页缓存加持吞吐很容易超过200万TPS。但要注意如果生产消费同时各压200万两张万兆网卡的进出流量会叠加网络就变成天花板了。我最终没有把“读写双满”作为压测目标因为生产环境里纯写入和纯读取很少在同一时刻、同一批broker上同时打满。容量评估只要确认生产和消费各自的能力上限再结合业务模型去预留水位就已经足够用了。6. 压测中最折磨人的几个坑排查链路完整还原6.1 网卡队列被小包打满CPU却在旁边看戏第一轮压测虽然只有70万TPS但看着监控我一度以为网卡要爆某个时刻eth0的入向流量已经到900MB/s。可broker的CPU才30%磁盘IO只有200MB/s。这说明请求在小包模式下把TCP协议栈喂饱了真正的数据吞吐还远着呢。排查顺序是这样的用mpstat -P ALL 1看各核心负载发现网卡中断全部集中在CPU0上用ethtool -S eth0看rx_packets和rx_bytes的比例确认就是大量小包开启网卡多队列并绑定中断把包处理压力分散到多个CPU再把batch和linger调上去小包数量骤降网卡流量反而从900MB/s降到了500MB/sTPS却从70万翻到120万。结论网卡“满”不一定是吞吐到顶可能是小包把队列打爆了。压测遇到网卡先满先看每秒包数pps别急着加网卡。6.2 写吞吐突然掉到300MB/s一个隐蔽的刷盘参数第二轮压测120万TPS跑着跑着TPS突然掉到100万以下。查iostatsdb的%util冲到90%以上写吞吐却只有300MB/s。这不是机械盘正常的表现倒像是有人在强行做随机写。后来定位到原因某个运维脚本把log.flush.interval.messages设成了10000等于每攒1万条消息就强制调用一次fsync。Kafka本来的设计是让操作系统自己决定刷盘时机这么一设等于把顺序追加写活生生改成了“反复落盘再等待确认”完全破坏了顺序IO的优势。把flush相关配置恢复成默认不主动刷盘同时把vm.dirty_ratio调低一点让内核的pdflush更均匀地工作磁盘吞吐立刻回到500MB/s以上TPS也随之稳定在120万以上。这个坑很有代表性很多“调优”脚本在网上抄来抄去反而把Kafka最核心的顺序IO模型给破坏了。遇到磁盘异常掉速优先检查刷盘和段清理配置。6.3 压测机自己成了瓶颈broker在等你从2台压测机扩展到4台时我发现第一台压测机的CPU总是打到100%TPS却稳定在40万出头怎么加线程都上不去。用perf top一看除了kafka-producer自身的逻辑还有一大堆线程在做日志打印和GC直接把CPU吃没了。压测机瓶颈的排查方法top看单核负载和GC线程JVM参数是否合理压测脚本里去掉多余输出关闭无关日志打印调整压测机JVM的-Xms和-Xmx减少GC停顿压测机上不要跑其他重负载服务别和broker抢资源。我经常看到有人直接在broker上满负荷压测结果压测机和broker互相争抢CPU数据忽高忽低。经验是压测机单独准备别图省事共用生产机器。6.4 分区数太少好几个broker在“干瞪眼”最后必查的一项是topic分区数。有一次测试中三个broker里一个CPU 80%另外两个CPU 20%负载严重不均。排查topic metadata发现所有压力都集中在少数几个分区上其中两个broker的主分区就扛了80%的写流量。原因很简单16个分区过少4台压测机×8线程的并发压力并不能均匀散开大量线程同时抢同一批分区。把分区数提到48之后三个broker的CPU很快就变得均匀了。建议压测前先算一笔账并发线程数小于等于分区数最好分区数是并发线程数的1.5倍以上这样调度才有余量。压测那几天我换了无数轮参数最后能稳定跑出200万TPS靠的不是哪一条参数多神奇而是每一轮都把“为什么上不去”这个问题回答清楚了。如果说有什么经验值得单独拎出来就是这几条先算流量账再选硬件压测时每次只动一组变量监控指标要看全套别单盯TPS。Kafka的高吞吐从来不是靠哪台机器多值钱而是把顺序IO、批处理、页缓存这几张牌打好。下次再遇见“要不要换更贵的服务器”这种讨论你可以先把本文的参数组合在自己的环境里试一遍再决定钱该怎么花。
阅读完成 · 觉得有帮助?
咨询建站