性能测试工具 Kafka 消息迁移工具的压测与调优

rihkddd · 2020年10月10日 · 最后由 Thirty-Thirty 回复于 2020年12月21日 · 9667 次阅读
本帖已被设为精华帖!

压测背景

临近双十一大家都免不了要对自己的业务系统进行压测。公司一个核心业务预计双十一会迎来数倍日常流量的业务高峰,该系统强依赖于 Kafka,Kafka 本身是分布式的系统,扩容比较方便。但是为了保证核心业务的稳定性和高可用,需要在机房故障的场景下核心业务快速恢复服务,因此 Kafka 需要跨机房热备机制。

一般情况的 Kafka 集群,都是在同一个 IDC 中的,跨 IDC 的热备在 Kafka 的使用场景叫做 Mirror,Kafka 内置的支持为 MirrorMaker,现在已经进化到了第二版,下文简称为 MM2.

经过前期技术调研,基本确定了 MM2 的 Kafka 热备机制。为了确定这套方案在双十一的流量高峰情况下,能满足消息系统热备的需求,需要提前做压测。

压测实施

MM2 的工作原理可以简单理解为:在目的端机房消费源端机房需要复制的消息。因此 MM2 的压测,本质上还是 Kafka 的压测。而幸运的是 Kafka 作为高性能、广泛使用的消息队列,它本身就提供了 benchmark 工具,可以方便的使用内置的命令行进行压测。

基本的用法如下:

./bin/kafka-producer-perf-test.sh --topic test_perf --num-records 1000000 --record-size 15000 --throughput -1 --producer-props bootstrap.servers=10.xx.xx.1:9094,10.xx.xx.2:9094 acks=1

几个重要的参数解释如下:

  • acks 消息确认模式,-1 表示需要确认所有副本都写入成功,1 表示只需确认一个副本写入成功,0 表示无需确认。
  • --topic topic 名称,本例为 test_perf
  • --num-records 总共需要发送的消息数,本例为 1000000
  • --record-size 每个记录的字节数,本例为 15000
  • --throughput 每秒钟发送的记录数,本例为 -1,表示不做限制
  • --producer-props bootstrap.servers=localhost:9092 发送端的配置信息,本例只指定了 kafka 的链接信息
  • --producer-num-retries 一个消息失败发送重试次数
  • --request-timeouts-ms 一个消息请求发送超时时间

消息体的大小,我们可以根据实际的业务场景构造合适的消息大小,例如统计一批消息的平均大小:

./bin/kafka-console-consumer.sh --bootstrap-server 10.xx.xx.1:9094,10.xx.xx.2:9094 --topic sdsoms  --max-messages=100 | wc

acks 模式需要和实际生产环境保持一致即可。

确定了消息体大小,和 acks 之后,可以调整 num-records 和 throughput,保证有足够的消息数量和速率。

性能问题

在具体压测实施过程中,遇到了不少问题。经过业务预估,双十一业务高峰时 Kafka 需要承受的 QPS 约为 x 万,但是初次压测下来的数据仅能到几千就出现了大量的跨机房复制消息延迟。

问题分析

通过对机器负载分析发现:

部署消息迁移服务的机器 CPU/磁盘都没有到平静,实际上从 MM2 消息迁移的原理来看,它工作完全是在内存中完成,因此磁盘基本没有使用。

那么最有可能的问题就出在了网络上。

跨 IDC 的消息同步会面临几个问题:

  1. IDC 之间的网络延迟(RTT)会远高于 IDC 内的,典型的情况下机房内的延迟往往在 0.1ms 以内,而广州到上海的 IDC 延迟一般为 30ms。
  2. 带宽有限,且成本高。跨 IDC 的网络铺设成本高,总带宽有限,费用很高。机房内部双千兆一般都是标配,万兆也很普及,而且一般是免费使用。

经过一番分析和计算,在压测的业务场景中,消息体较大,达到了 15000Byte(典型的消息应用场景中消息体一般在几十到几百 Byte),这个大小在几千的 QPS 下,带宽就需要几百兆大小。

为了确定是带宽的原因,一方面同 OP 确定跨机房的带宽上限。另一方面在两个机房的机器上安装 iperf3 工具测试速度。

使用 iperf3 工具测试网络速度:

# MM2目的端的机器(也就是MM2服务部署机器)操作
iperf3 -s
# MM2源端的机器操作
iperf3 -c MM2_host_ip

综合测速结果和 OP 反馈的机房间带宽,发现确实到了带宽瓶颈。

所以向 OP 申请临时调高带宽,带宽调高了 X 倍。

本以为,机房间带宽增加之后,MM2 的性能应该也会有接近 X 倍的提升。但是实际情况却是:
在性能有小幅提升后,在 CPU/磁盘/网络依然没有到达瓶颈的情况下,再出出现了大量的跨机房消息复制延迟。

经过一番深入分析发现,在高延迟的网络环境中,打满带宽不是一件显而易见的事情:

我们知道 Kafka 使用的是 TCP 网络传输协议,TCP 在经过三次握手之后,开始进入的流量控制阶段,在这个阶段,会使用滑动窗口确定一个 RTT 时间范围内发送的数据量。在一个 TCP 连接中,如果网络延迟很大,那么它实际上不是那么容易能完全利用带宽。

这里引入一个概念:

带宽时延乘积,在数据通信中,带宽时延乘积(英语:bandwidth-delay product;或称带宽延时乘积、带宽延时积等)指的是一个数据链路的能力(每秒比特)与来回通信延迟(单位秒)的乘积。其结果是以比特(或字节)为单位的一个数据总量,等同在任何特定时间该网络线路上的最大数据量——已发送但尚未确认的数据。

当然,在现代的高延迟、大带宽网络中 TCP 提供了缩放因子来解决这个问题,可以避免 TCP 默认最大窗口 65535 带来的带宽利用不足问题。

沿着这个思路,我们对服务的网络相关参数做了一些调整,主要增加了 tcp 相关 buffer 大小,虽然带来了一些提升,但是离我们的目标还很远。这是因为,考虑一个线路的传输速率时,除了需要考虑 BDP 这个因素外,还需要考虑丢包率,滑动窗口增加,在增加发送速率的同时,也增加了丢包重传的成本。

性能优化

在带宽上限提不上去的情况下,还想提升 QPS,那么可行的方案就是减小消息体大小。减小消息体有两个思路:

  1. 压缩。
  2. 消息裁剪。

压缩

压缩就是在消息生产之后,进入 producer 时,进行压缩,之后的同集群落盘和跨集群 MM2 复制,就都是压缩后的消息了。producer 和 consumer 一般都内置了消息压缩的支持,Kafka 支持 GZIP、Snappy、LZ4 三种压缩算法,从 2.1.0 开始正式支持 zstd 算法。各种压缩算法之间的对比可以参考这个文章:http://zhongmingmao.me/2019/08/02/kafka-compression/

在我们的使用场景中,我们希望尽可能的提高压缩比,降低网络传输量,虽然 zstd 具有最高的压缩比,不幸的时我们使用的版本尚不支持,因此只能退而求其次选择了 GZIP 算法。但即使时 GZIP 也能获得很高的压缩比。

消息剪裁

原来的消息体很大主要的原因是,很多数据其实不需要,但是为了简单方便,都放到了消息中,通过仔细分析消息字段,并去除不需要的字段,大幅减小了消息体大小。

通过上述两种优化的叠加,基本上达到了预期的压测目标,希望消息系统可以顺利通过双十一的考验。


参考资料

[1] https://engineering.salesforce.com/mirrormaker-performance-tuning-63afaed12c21
[2] https://plantegg.github.io/2019/09/28/TCP--性能和发送接收 Buffer 的关系/就是要你懂
[3] http://www.dengshenyu.com/kafka-data-mirror/
[4] https://www.slideshare.net/JiangjieQin/producer-performance-tuning-for-apache-kafka-63147600

共收到 12 条回复 时间 点赞

楼主为了绕过 tcp 流量控制,减少了消息体的大小.这样会不会和生产环境的场景有出入呢?根据楼主所说生产环境的消息体是要比压测时大的,不考虑消息体的大小最后得到的 kafka 吞吐量偏差大不大.还有没有别的解决方式?

昨天有雨 回复
  1. 没有绕过 tcp 流量控制一说,写这个文章一个原因是高延迟带宽对传输速率的影响这个应该很少有人注意到(我也是第一次遇到这类问题),跨机房的传输速率只能到那么高(而且费用很高),所以合理的方式就是降低传输量。
  2. 压测就是模拟的生产的某个业务场景,消息体的大小是一个很关键的参数,这个是从生产环境统计了一部分消息获取的。文中提到的优化肯定是都应用到了生产环境的。
  3. 别的解决方式,增加并发度方向:可以增大 Kafka partition 数量,但是不是越大越好,有一定的副作用。进一步减少网络传输量考虑:可以使用更紧凑的数据格式,例如 pb,但是这需要在 consumer 端做一定的开发。
rihkddd 回复

谢谢耐心解答,还看了另外一个关于 java 内存泄露的帖子 都很赞

发现一个错别字,性能优化下面的应该是 “压缩 “吧,写成了” 压测 “
想知道一下,gzip 压缩后能够提升多少性能

花菜 回复

多谢指出,已修正。具体的压缩比根消息内容和一批消息的大小有比较大关系,在我们的场景压缩比能到 7:1~10:1,快一个数量级了。

陈恒捷 将本帖设为了精华贴 10月11日 23:24

完整的压测实践及问题解决思路,赞

感谢分享,赞

挺好,赞

仅楼主可见

消息剪裁时去除了不需要的字段,多大幅度减小了消息体的大小?能不能也给出个范围。

Thirty-Thirty 回复

这个跟你业务强相关的,你就直接对比一条裁剪之后和原来的消息字节数就能得到。在我们这个场景大约能减少 70%,但是没有什么参考价值。

rihkddd 回复

我明白这和具体业务强相关,就是你既然提到你们当时两种方式里压缩比是多大范围,所以也想知道消息剪裁的减小幅度。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册