首先让我先来介绍一下这个“炼狱”究竟是用来做什么用的。Broker的一项主要工作就是接收并处理网络上发来的Request。这些Request其中有一些是可以立即答复的,那很自然这些Request会被直接回复。另外还有一部分是没办法或者Request自发的要求延时答复(例如发送和接收的Batch),Broker会把这种Request放入Paurgatory当中,同时每一个加入Purgatory当中的Request还会额外的加入到两个监控对队列:
WatcherFor队列:用于检查Request是否被满足。
DelayedQueue队列:用于检测Request是否超时。
Request最终的状态只有一个,就是Complete。请求被满足和超时最终都会被统一的认为是Complete。
目前版本的Purgatory设计上是存在一定缺陷的。Request状态转变为Complete后,并没能立即从Purgatory中移除,而是继续占用资源,因此占用内存累积最终会引发OOM。这种情况一般只会在topic流量较少的情况下触发。更详细的资料可以查阅扩展阅读,在此不做展开。
在实际使用中我也是踩了这个坑过来的,当时的情况是集群新上了一个topic,初期该topic数据很少(Low volume topic),导致那段时间在凌晨3,4点左右会随机有Broker因为OOM挂掉。定位原因后把*.purgatory.purge.interval.requests的配置调整小至100就解决了这个问题。
Kafka的研发团队已经开始着手重新设计Purgatory,力求能够让Request在Complete时立即从Purgatory中移除。
log.flush.interval.ms:Long.MaxValuelog.flush.scheduler.interval.ms:Long.MaxValuelog.flush.interval.messages:Long.MaxValue
Flush相关的配置参数控制着Broker写盘的频率,一般无需改动。如果topic的数据量较小可以考虑减少log.flush.interval.ms和log.flush.interval.messages来强制刷写数据,减少可能由于缓存数据未写盘带来的不一致。
in.insync.replicas:1
这个参数只能在topic层级配置,指定每次Producer写操作至少要保证有多少个在ISR的Replica确认,一般配合request.required.acks使用。要注意,这个参数如果设置的过高可能会大幅降低吞吐量。
compression.codec:none
Message落地时是否采用以及采用何种压缩算法。一般都是把Producer发过来Message直接保存,不再改变压缩方式。
Producer" style="font-weight: 400; font-size: 16px; color: rgb(0, 0, 0); font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif; line-height: 25.6px;">Producer
buffer.memory:33554432 (32m)
在Producer端用来存放尚未发送出去的Message的缓冲区大小。缓冲区满了之后可以选择阻塞发送或抛出异常,由block.on.buffer.full的配置来决定。
compression.type:none
默认发送不进行压缩,推荐配置一种适合的压缩算法,可以大幅度的减缓网络压力和Broker的存储压力。
linger.ms:0
Producer默认会把两次发送时间间隔内收集到的所有Requests进行一次聚合然后再发送,以此提高吞吐量,而linger.ms则更进一步,这个参数为每次发送增加一些delay,以此来聚合更多的Message。
batch.size:16384
Producer会尝试去把发往同一个Partition的多个Requests进行合并,batch.size指明了一次Batch合并后Requests总大小的上限。如果这个值设置的太小,可能会导致所有的Request都不进行Batch。
acks:1
这个配置可以设定发送消息后是否需要Broker端返回确认。
0: 不需要进行确认,速度最快。存在丢失数据的风险。
1: 仅需要Leader进行确认,不需要ISR进行确认。是一种效率和安全折中的方式。
all: 需要ISR中所有的Replica给予接收确认,速度最慢,安全性最高,但是由于ISR可能会缩小到仅包含一个Replica,所以设置参数为all并不能一定避免数据丢失。
注:新老Producer的参数有很大不同,其他配置含义可以对照参考Kafka官方文档。
Consumer
num.consumer.fetchers:1
启动Consumer的个数,适当增加可以提高并发度。
fetch.min.bytes:1
每次Fetch Request至少要拿到多少字节的数据才可以返回。
fetch.wait.max.ms:100
在Fetch Request获取的数据至少达到fetch.min.bytes之前,允许等待的最大时长。对应上面说到的Purgatory中请求的超时时间。
性能测试实战
由于可调整的配置参数较多,为了可以准确的展示不同配置对性能产生的影响,我们每次只调整一个参数,观察对照组结果。测试工具使用Kafka提供的Performance工具ProducerPerformance和ConsumerPerformance。
Producer
Kafka在0.8版本推出了新的Producer Client,较之前版本有极大的性能提升,所以后续的示例无需说明都采用的是新Producer,这里就只给出一组新旧Producer的对照组数据。
其中,Producer的message.size为1024,不压缩,测试时都发送500000条Message。相信大家看过上面结果,就很清楚以后为什么要乖乖地用新设计的Producer来发消息了。
Kafka发布时提供了两个Producer的性能测试工具:
kafka.tools.ProducerPerformance (Scala)
org.apache.kafka.clients.tools.ProducerPerformance (Java)
两份工具的大体功能类似。通过Scala版的代码可以很方便的输出CVS文件,通过Patch:1190(https://issues.apache.org/jira/browse/KAFKA-1190)中包含的一个R脚本可以将这个CVS文件结果可视化。
注:如果使用Scala版代码,不建议开启--vary-message-size功能。这个功能使得每次构造消息时都会在内部调用random方法生成随机长度的消息,尤其是在进行压力测试时,构造随机串的消耗累计占比飙高,严重影响发送效率最终致使测试结果失准。
下面,从thread, acks, linger.ms, replica, compression几个主要维度测试了一下Producer的组合性能表现。其中,公共指标如下:
message.size=1024
batch.siz=10240
message.count=50000000
测试结果如下:
注:部分提高了linger.ms的Case效果不明显是由于触发了其他的flush条件。
Consumer
Consumer的测试相对来说就简单很多,毕竟拉取数据时只从Leader读,无论多少Replica都是如此。所以比较关键的参数就聚焦到了fetch.size和thread上。
上述本文给出的参数只是一种参考,适用于我们的集群配置。大家有兴趣可以根据上面提供的方法,在自己的集群上新建独立topic,在实际环境中测试,这样得出的配置才是最适合你的配置。希望大家都能通过上面的方法把自己手头的Kafka调教好,榨干最后一丝性能。
扩展阅读
Request Purgatory潜在引发OOM的问题: https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=34839465
Purgatory Redesign: https://cwiki.apache.org/confluence/display/KAFKA/Purgatory+Redesign+Proposal
深入理解G1内存收集器: http://t.cn/RAUulGC
How to choose the number of topics/partitions in a Kafka cluster?: http://www.confluent.io/blog/how-to-choose-the-number-of-topicspartitions-in-a-kafka-cluster
Tips for improving performance of kafka producer: http://ingest.tips/2015/07/19/tips-for-improving-performance-of-kafka-producer/
Kafka 的详细介绍:请点这里
Kafka 的下载地址:请点这里