上一篇文章《Kafka 高吞吐量性能揭秘 》介绍了Kafka在设计上是如何来保证高时效、大吞吐量的,主要的内容集中在底层原理和架构上,属于理论知识范畴。这次我们站在应用和运维的角度,聊一聊集群到位后要怎么才能最好的配置参数和进行测试性能。Kafka的配置详尽且复杂,想要进行全面的性能调优需要掌握大量信息,我也只是通过工作中的一些实战经验来筛选出对集群性能影响最大的几个要点,接下来要阐述的观点也仅限于我所描述的环境下,请大家根据自己的环境适当取舍。
今天的文章分为两大部分,第一部分介绍一下我总结的跟性能有关的一些参数、含义以及调优策略。第二部分会给出一些我自己实践过的测试结果对照组,具体的数值和结果可能因场景、机器、环境而异,但是总体的思路和方法应该是一致的。
在正式进入主题之前,介绍一下本次测试所使用的机器配置:
6台物理机,其中三台部署Broker,三台专门用来launch request。
每台物理机:24 Processors,189G Memory,2G 单机带宽。
执行本次测试时为了能够覆盖到到一些“非常规”的用法,我把Broker的HeapSize设置到了30G。
相关参数介绍
在调试和优化使用Java开发的系统时,第一步肯定绕不开对JVM的调优,Kafka自然也不例外,而JVM调优的重点则是在内存上。
其实Kafka服务本身并不需要很大内存,上篇文章也已经详细介绍过Kafka依赖系统提供的PageCache来满足性能上的要求,利用VisualJVM等工具可以很清晰的分析出Heap Space的占用比例情况。本文中测试时设置30G内存的目的是支持更高的并发,高并发本身就必然会需要更多的内存来支持,同时高并发也意味着SocketBuffer等相关缓存容量会成倍增长。实际使用中,调整内存大小的准则是留给系统尽可能多的空闲内存,Broker本身则是够用就好。
说完了大小设置我们再来聊一下JVM上的垃圾回收器,官方文档里推荐使用最新的G1来代替CMS作为垃圾回收器。不过也明确指出在某些低版本(1.7u21)的JDK上还是会存在一些不稳定的问题。推荐使用的最低版本为JDK 1.7u51。下面是本次试验中Broker的JVM内存配置参数:
-Xms30g -Xmx30g -XX:PermSize=48m -XX:MaxPermSize=48m -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35
其实G1早在JDK 1.6u14中就已经作为体验版首次被引入,但是由于最初误宣传需要收费才能使用,和其自身尚不稳定存在Bug等因素,一直等到1.7的后期update版本才逐渐走入我们的视野。
G1相比较于CMS的优势:
G1是一种适用于服务器端的垃圾回收器,很好的平衡了吞吐量和响应能力。
对于内存的划分方法不同,Eden, Survivor, Old区域不再固定,使用内存会更高效。G1通过对内存进行Region的划分,有效避免了内存碎片问题。
G1可以指定GC时可用于暂停线程的时间(不保证严格遵守)。而CMS并不提供可控选项。
CMS只有在FullGC之后会重新合并压缩内存,而G1把回收和合并集合在一起。
CMS只能使用在Old区,在清理Young时一般是配合使用ParNew,而G1可以统一两类分区的回收算法。
G1的适用场景:
JVM占用内存较大(At least 4G)
应用本身频繁申请、释放内存,进而产生大量内存碎片时。
对于GC时间较为敏感的应用。
接下来,我们来总结一下Kafka本身可能会对性能产生影响的配置项。
Broker
num.network.threads:3
用于接收并处理网络请求的线程数,默认为3。其内部实现是采用Selector模型。启动一个线程作为Acceptor来负责建立连接,再配合启动num.network.threads个线程来轮流负责从Sockets里读取请求,一般无需改动,除非上下游并发请求量过大。
num.partitions:1
Partition的数量选取也会直接影响到Kafka集群的吞吐性能。例如我写过MapReduce任务从Kafka中读取数据,每个Partition对应一个Mapper去消费数据,如果Partition数量太少,则任务会因为Mapper数不足而非常慢。此外,当Partition数量相对于流入流出的数据量显得较少,或由于业务逻辑和Partition数量没有匹配好造成个别Partition读写数据量大,大量的读写请求集中落在一台或几台机器上时,很容易就会打满NIC的全部流量。不难想象这时不仅这一个Partition的读写会出现性能瓶颈,同Broker上的其他Partition或服务都会陷入一个网络资源匮乏的情况。
queued.max.requests:500
这个参数是指定用于缓存网络请求的队列的最大容量,这个队列达到上限之后将不再接收新请求。一般不会成为瓶颈点,除非I/O性能太差,这时需要配合num.io.threads等配置一同进行调整。
相关阅读:
Replica相关配置:
replica.lag.time.max.ms:10000replica.lag.max.messages:4000num.replica.fetchers:1