3BB0DD8E-7440-422F-9070-14A0CD3FF8BA.png

请仔细了解这张图,尤其注意有标志的几个关注点。我们会不止一次回到这张图上

背景

Kafka到底能够应用在高可用的业务上?官方给出的答案是肯定的,最新版,已经支持消息队列的事务,但我们对其性能是有疑问的。
Kafka根据配置的ACK级别,其性能表现将特别大,为了找到其适用场景,特做此测试,以便应用kafka时能够灵活应对。
测试过程还探讨了许多丢消息的场景。相对于大多数仅仅针对kafka集群本身的测试,本测试还介绍了丢消息的业务场景。整个方案应该是一个整体,才能够达到最高级别的高可用,不因该区别对待。

测试目标

  • 集群高可用,以及需要满足高可用时需要的最小集群大小和相关配置以及限制等
  • 消息不丢失,以及为了满足消息不丢失需要的配置和约定等
  • 测试环境

broker:

3台机器
8 core 
16G 
1T SSD 
Centos 6.8
kafka_2.12-0.10.2.0 
broker jvm参数配置:Xms=8G Xmx=8G

client:

8 core 
16G 
Centos 6.8
原创文章,转载注明出处 (http://sayhiai.com)

测试场景

集群高可靠性配置:

zookeeper.connection.timeout.ms=15000
zookeeper.session.timeout.ms=15000
default.replication.factor=3
num.partitions=6
 min.insync.replicas=2
 unclean.leader.election.enable=false
log.flush.interval.ms=1000

ack

acks= all
 retries = 3
 request.timeout.ms=5000

消息大小:1024byte

原创文章,转载注明出处 (http://sayhiai.com)

failover 测试

测试方法

下线一个节点,测试故障的恢复时间和故障期间的服务水平

测试过程

将 replica.lag.time.max.ms 从 10s 调整为 60s(延长时间方便观察),然后 kill Broker 0,挑选 3 个 partition,观察 ISR 变化如下:
其中,第二 / 三阶段入队成功率受损:

  • 第二阶段期间,Partition 96/97/98 均无法写入,入队成功率成功率下降至 0%。
  • 第三阶段期间,Partition 96 可继续写入,但 Partition 97/98 无法写入,因为写入要等 Broker 0 回 ack,但 Broker 0 已 kill,入队成功率下降至 33%。

而实际观察,第二 / 三阶段期间完全没吞吐,原因是压测工具不断报连接失败,停止了写入。

原因分析

Kafka Broker leader 是通过 Controller 选举出来的,ISR 列表是 leader 维护的。
前者的的租约是 Controller 定义的,后者的租约是 Broker 配置 replica.lag.time.max.ms 指定的。
所以,第二阶段持续时间较短,是 Controller 的租约时间决定的,第三阶段持续时间较长,是 replica.lag.time.max.ms 决定的。
当 Broker 0 被 kill 时,前者影响本来 Broker 0 是 leader 的 1/3 partitions 的入队成功率,后者影响 Broker 0 作为 follower 的 2/3 partitions 的入队成功率。

HA结论

kafka在failover期间,会有大约10秒的不可用时间,该时间由 replica.lag.time.max.ms 决定。因此应用程序需要处理此种情况下的异常信息,设置合理的重试次数和退避算法。

原创文章,转载注明出处 (http://sayhiai.com)

压力测试

测试方法

测试脚本:

./kafka-producer-perf-test.sh --topic test003 --num-records 1000000 --record-size 1024  --throughput -1 --producer.config ../config/producer.properties

测试结果

不限制并发吞吐量

[root@l-monitor-logstash2.pub.prod.aws.dm bin]# time ./kafka-producer-perf-test.sh --topic ack001 --num-records 1000000 --record-size 1024 --throughput -1 --producer.config ../config/producer.properties
[2017-09-14 21:26:57,543] WARN Error while fetching metadata with correlation id 1 : {ack001=LEADER_NOT_AVAILABLE} (org.apache.kafka.clients.NetworkClient)
81112 records sent, 16219.2 records/sec (15.84 MB/sec), 1416.2 ms avg latency, 1779.0 max latency.
92070 records sent, 18414.0 records/sec (17.98 MB/sec), 1671.7 ms avg latency, 1821.0 max latency.
91860 records sent, 18368.3 records/sec (17.94 MB/sec), 1670.3 ms avg latency, 1958.0 max latency.
91470 records sent, 18294.0 records/sec (17.87 MB/sec), 1672.3 ms avg latency, 2038.0 max latency.
91050 records sent, 18202.7 records/sec (17.78 MB/sec), 1678.9 ms avg latency, 2158.0 max latency.
92670 records sent, 18534.0 records/sec (18.10 MB/sec), 1657.6 ms avg latency, 2223.0 max latency.
89040 records sent, 17808.0 records/sec (17.39 MB/sec), 1715.0 ms avg latency, 2481.0 max latency.
86370 records sent, 17274.0 records/sec (16.87 MB/sec), 1767.5 ms avg latency, 2704.0 max latency.
91290 records sent, 18254.3 records/sec (17.83 MB/sec), 1670.2 ms avg latency, 2553.0 max latency.
92220 records sent, 18444.0 records/sec (18.01 MB/sec), 1658.1 ms avg latency, 2626.0 max latency.
90240 records sent, 18048.0 records/sec (17.63 MB/sec), 1669.9 ms avg latency, 2733.0 max latency.
1000000 records sent, 17671.591150 records/sec (17.26 MB/sec), 1670.61 ms avg latency, 2764.00 ms max latency, 1544 ms 50th, 2649 ms 95th, 2722 ms 99th, 2753 ms 99.9th.
real 0m57.409s
user 0m14.544s
sys 0m2.072s

限制吞吐量 1w

[root@l-monitor-logstash2.pub.prod.aws.dm bin]# time ./kafka-producer-perf-test.sh --topic ack003 --num-records 1000000 --record-size 1024 --throughput 10000 --producer.config ../config/producer.properties
[2017-09-15 10:51:53,184] WARN Error while fetching metadata with correlation id 1 : {ack003=LEADER_NOT_AVAILABLE} (org.apache.kafka.clients.NetworkClient)
[2017-09-15 10:51:53,295] WARN Error while fetching metadata with correlation id 4 : {ack003=LEADER_NOT_AVAILABLE} (org.apache.kafka.clients.NetworkClient)
49766 records sent, 9953.2 records/sec (9.72 MB/sec), 34.9 ms avg latency, 358.0 max latency.
50009 records sent, 10001.8 records/sec (9.77 MB/sec), 23.9 ms avg latency, 39.0 max latency.
50060 records sent, 10008.0 records/sec (9.77 MB/sec), 23.9 ms avg latency, 49.0 max latency.
49967 records sent, 9991.4 records/sec (9.76 MB/sec), 23.6 ms avg latency, 38.0 max latency.
50014 records sent, 10000.8 records/sec (9.77 MB/sec), 24.0 ms avg latency, 51.0 max latency.
50049 records sent, 10007.8 records/sec (9.77 MB/sec), 23.5 ms avg latency, 37.0 max latency.
49978 records sent, 9995.6 records/sec (9.76 MB/sec), 23.5 ms avg latency, 44.0 max latency.
49803 records sent, 9958.6 records/sec (9.73 MB/sec), 23.7 ms avg latency, 47.0 max latency.
50229 records sent, 10045.8 records/sec (9.81 MB/sec), 23.6 ms avg latency, 46.0 max latency.
49980 records sent, 9996.0 records/sec (9.76 MB/sec), 23.5 ms avg latency, 36.0 max latency.
50061 records sent, 10010.2 records/sec (9.78 MB/sec), 23.6 ms avg latency, 36.0 max latency.
49983 records sent, 9996.6 records/sec (9.76 MB/sec), 23.4 ms avg latency, 37.0 max latency.
49978 records sent, 9995.6 records/sec (9.76 MB/sec), 23.9 ms avg latency, 55.0 max latency.
50061 records sent, 10012.2 records/sec (9.78 MB/sec), 24.3 ms avg latency, 55.0 max latency.
49981 records sent, 9996.2 records/sec (9.76 MB/sec), 23.5 ms avg latency, 42.0 max latency.
49979 records sent, 9991.8 records/sec (9.76 MB/sec), 23.8 ms avg latency, 39.0 max latency.
50077 records sent, 10013.4 records/sec (9.78 MB/sec), 23.6 ms avg latency, 41.0 max latency.
49974 records sent, 9994.8 records/sec (9.76 MB/sec), 23.4 ms avg latency, 36.0 max latency.
50067 records sent, 10011.4 records/sec (9.78 MB/sec), 23.8 ms avg latency, 65.0 max latency.
49963 records sent, 9992.6 records/sec (9.76 MB/sec), 23.5 ms avg latency, 54.0 max latency.
1000000 records sent, 9997.300729 records/sec (9.76 MB/sec), 24.24 ms avg latency, 358.00 ms max latency, 23 ms 50th, 28 ms 95th, 39 ms 99th, 154 ms 99.9th.
real 1m40.808s
user 0m16.620s
sys 0m1.260s
更多...
吞吐量5k 1000000 records sent, 4999.275105 records/sec (4.88 MB/sec), 22.94 ms avg latency, 127.00 ms max latency, 23 ms 50th, 27 ms 95th, 31 ms 99th, 41 ms 99.9th.
吞吐量2w 1000000 records sent, 18990.827430 records/sec (18.55 MB/sec), 954.74 ms avg latency, 2657.00 ms max latency, 739 ms 50th, 2492 ms 95th, 2611 ms 99th, 2650 ms 99.9th.
吞吐量3w 1000000 records sent, 19125.212768 records/sec (18.68 MB/sec), 1527.07 ms avg latency, 3020.00 ms max latency, 1582 ms 50th, 2815 ms 95th, 2979 ms 99th, 3011 ms 99.9th.

12分区,2.6w吞吐量

[root@l-monitor-logstash2.pub.prod.aws.dm bin]# time ./kafka-producer-perf-test.sh --topic ack001 --num-records 1000000 --record-size 1024 --throughput 26000 --producer.config ../config/producer.properties
129256 records sent, 25840.9 records/sec (25.24 MB/sec), 31.9 ms avg latency, 123.0 max latency.
129794 records sent, 25953.6 records/sec (25.35 MB/sec), 28.6 ms avg latency, 73.0 max latency.
130152 records sent, 26025.2 records/sec (25.42 MB/sec), 28.3 ms avg latency, 64.0 max latency.
130278 records sent, 26045.2 records/sec (25.43 MB/sec), 28.1 ms avg latency, 55.0 max latency.
130106 records sent, 26010.8 records/sec (25.40 MB/sec), 27.9 ms avg latency, 45.0 max latency.
130080 records sent, 26005.6 records/sec (25.40 MB/sec), 27.7 ms avg latency, 41.0 max latency.
130093 records sent, 26013.4 records/sec (25.40 MB/sec), 74.5 ms avg latency, 343.0 max latency.
1000000 records sent, 25904.051394 records/sec (25.30 MB/sec), 38.33 ms avg latency, 343.00 ms max latency, 28 ms 50th, 122 ms 95th, 242 ms 99th, 321 ms 99.9th.
real 0m39.395s
user 0m12.204s
sys 0m1.616s

cpu与内存无任何变化。网络rx/tx :170Mbps/120Mbps,磁盘IoUtil: 6%。1百万数据能在2分钟内完成。

压测结论

影响提交效率的原因主要有:partition数量 + 超时时长 + 消息大小 + 吞吐量

  • 不做限制:ack=all的模式,不限制吞吐量,TPS能够保持在2w左右,平均耗时在1600ms左右,99.9%的记录能够两秒左右正常提交反馈,最大耗时有记录超过5秒。
  • 超时时长:当将超时时常设置为5秒以上时,提交全部成功(ack)。将超时逐步降低到3秒左右,陆续会有大量超时出现。官方的默认值为30秒,考虑到网络环境的复杂性,建议将此参数设置成10秒,如还有超时,需要客户端捕获异常进行特殊处理。
  • 消息大小:当将消息大小设置为512byte,提交的TPS能够打到3w/秒;当增加到2k左右,TPS降低到9k/s,消息大小与TPS成线性关系。
  • 流量:当限制吞吐量为1.3w左右,减少竞争,效果最佳。平均耗时降低到24毫秒,最大延迟仅300多毫秒,服务水平相当高。
  • 分区数量:增加分区数能显著提高处理能力,但分区数会影响故障恢复时间。本测试用例仅针对6分区的情况,测试证明,当分区数增加到12,处理能力几乎增加一倍,但继续增加,性能不会再有显著提升。

最终结论:假定网络状态良好,在ack=all模式、超时10秒、重试3次、分区为6的情况下,能够承受1.3w/s的消息请求,其写入平均耗时不超过30ms,最大耗时不超过500ms。想要增加TPS,可以增加partition到12,能够达到2.6w/s的高效写入。

堆积测试

kafka生产和消费理论上不受消息堆积影响,消息堆积只是占用磁盘空间,这里的消息堆积是指topic中的消息数,和消息是否消费无关

原创文章,转载注明出处 (http://sayhiai.com)

结论

kafka采用基于时间的SLA(服务水平保证),重要消息保存3天。

性能

基本配置:消息1k大小,ack=all,即所有副本都同步的情况。为确保消息可靠,全部采用3个副本。

  • 3副本,1个partition的情况:6k-8k
  • 3副本,6个partition的情况:1.3w-1.6w
  • 3副本,12个partion的情况:2.6w-2.8w

注意:生产端,考虑一种场景,单条发送,然后调用future.get()确认,TPS会急剧降低到2k以下,请确认确实需要这么做,否则,使用异步提交,callback调用的方式。相对于ACK模式1.6w的TPS,普通模式提交,能够达到13w(主要是网络和IO瓶颈,带宽占满)。当吞吐量限制在1w左右并且开启ACK(非常符合我们的业务特征),kafka是高效且高可用的,平均耗时仅24毫秒,生产者的最佳实践是将超时设置成10秒,重试3次。消费者同样是高效的,6个partition、ack模式,平均耗时在20毫秒左右,具体处理耗时取决于消费端的处理能力。

kafka消息可靠性

  • 写3个副本,开启ack=all模式,每1秒刷一次磁盘。一条消息要经历Client --> Leader →Replica这个过程。leader等待所有的replica的ack应答,然后ack给Client端,整个过程多次确认;ack失败的消息,会再次重试,此模式能保证数据不丢失。要想达到此种消息级别,请务必按照架构组提供的最佳实践进行配置(kafka不同版本间参数相差很多)。
  • 消息传递有三种模式,kafka同步发送是At least one模式(0.10版)。消费端,要做幂等处理。可能产生重复消息的场景为:生产端发送了消息到leader节点,leader节点同步到所有follower节点并得到确认,此时leader节点当机,未将ack返回给生产端,生产端此时会尝试重发消息。然后follower节点中某台机器提升为leader,重复的数据由此产生。

扩容,故障的影响

  • 单节点当机,短暂影响生产消费,故障恢复时间与leader选举时间与partition数量有关(约10秒isr探测时间)。使用ACK模式,配合重试,能够保证故障期间数据不丢失。上图的2位置。
  • 扩容,等同于节点上线,不影响使用方。但节点到达可用状态,与整体落后数据量相关(简单的网络拷贝过程)。根据经验,部分消息拉取时间会变长,但影响不大。压测过程无明显抖动。建议消费端设置较长的超时来进行处理(包括异步处理情况)。上图的3位置。
  • =2节点当机(机房断电等),服务不可用。故障恢复需要两个节点达到同步状态,与整体数据量相关。磁盘每秒fsync,极端情况(全部当机),最多会丢失1秒数据。

原创文章,转载注明出处 (http://sayhiai.com)

什么时候会丢数据

  • 使用batch模式发送,缓冲区有数据时没有优雅关闭,此时缓冲区中数据会丢失。上图1位置。
  • 使用batch模式消费,拉取消息后,异步使用线程池处理,如果线程池没有优雅关闭,此时消费数据会丢失。上图4位置。
原创文章,转载注明出处 (http://sayhiai.com)

风险

  • 压测TPS仅作参考,实际运行中受网络延迟,坏盘、高低峰流量等影响,服务会有抖动。生产和消费端务必将所有处理失败的消息进行记录,以便极端情况下进行数据回放。
  • 消息中请勿传递大块不必要数据,消息大小对服务质量有直接线性影响。(请保持消息<2kb)
  • 消费端消费,除考虑幂等,不正确的异步线程池使用(比如使用了无界队列),经常造成消费端故障,请谨慎消费。
  • 如分配了6个partition,如果你有7台消费机器,其中有一台会是空闲的。设计时请考虑kafka的限制。
  • 默认kafka生产端开启了batch提交模式,也就是说,如果此时你的生产者当了,buffer中的消息会丢。请确保:生产者使用"kill -15"杀进程以给服务flush的机会;同时,如果你的消息很重要,请同时写入到日志文件中。 请权衡利弊再确认使用。

订阅微信公众号,小姐姐,教你玩架构~