问题背景html
项目中将Kafka接口进行RESTful封装,在使用RESTful接口进行性能测试时,发现Topic数增多后,开启SSL与非SSL进行测试,发现开启SSL后性能降低得厉害。例如600个Topic总数每一个Topic3分区3副本的场景下,使用1200个线程只发送10个Topic,开启SSL的TPS只有3100,可是不开启SSL性能达到11000。java
其中测试客户端会启动多个线程,每一个线程采用同步发送的方式调用RESTful API发送,即每次发送成功一条后才发送下一条。 客户端会根据发送线程在Topic数之间进行均分,例如1200个线程发送10个Topic,则每一个Topic同时有120个线程进行发送。git
定位与分析过程github
1.SSL性能降低算法
1.定位分析安全
开启SSL是会致使性能降低的, 主要来自于CPU的耗时与JVM的具体实现,参见Kafka官网的解释:网络
从咱们以前测试的结果来看,高可靠场景SSL性能降低并无太厉害(从2.3W TPS降低到2.1W TPS)。应该是触发了某些其余问题。经过JStack查看启动SSL下的堆栈,发现存在一些发送线程被Block住:并发
这个堆栈里面作的事情,是来自于java.security.SecureRandom要产生随机数,采用”SHA1PRNG”算法。在sun/oracle的jdk里,这个随机算法的的实如今底层依赖到操做系统提供的随机数据,默认用的是/dev/random,在读取时,/dev/random设备会返回小于熵池噪声总数的随机字节。/dev/random可生成高随机性的公钥或一次性密码本。若熵池空了,对/dev/random的读操做将会被阻塞,直到收集到了足够的环境噪声为止。这个问题在网上也查到,主要是JDK提供的SecureRandom函数存在1个全局的锁,在熵源不足且SSL线程多的时候很容易碰到这个问题,具体见:oracle
https://github.com/netty/netty/issues/3639dom
http://bugs.java.com/view_bug.do?bug_id=6521844
2.解决措施
措施一:更新JDK
目前这个问题是在OpenJDK 1.8中解决了,能够经过升级JDK到使用OpenJDK,可是这个方案不太好替换,而且OpenJDK和原来有什么不兼容还不清楚。
措施二:采用非阻塞的熵源: /dev/urandom
经过设置-Djava.security.egd=file:/dev/./urandom宏,随机数时选择/dev/urandom,它会重复地使用熵池中的数据以产生伪随机数据避免阻塞,不过随机安全性会下降。
2.Topic多状况下性能降低
1.定位分析
发如今Topic600个状况下,非SSL与SSL的时延其实差距并无原先发现的问题那么大,如下是咱们用SDK接口测试的时延数据:
600个 Topic总量下,400个线程同时发送10个Topic,非SSL与SSL时延对比:
能够看出时延差距在20%以内,主要的时延增长来自于Topic增多致使的。
为何Topic增多会致使时延增多?针对这个问题经过在程序进行打点测试,如下是在不一样的Topic数量状况下,针对10个Topic,总发送5000条消息的场景下,非SSL时延对比:
其中总时延 = 消息的待发送队列等待时延 + 服务端处理平均时延 + 网络发送与响应时延。
从上面的表格能够看出基本上每一个处理环节上都增长了时延4~5倍。为何会出现这种状况?分析以下可能点:
一、磁盘的写速度变慢
二、Server因为Topic多须要过滤信息变慢
三、复制处理在多Topic下变慢。即便无数据,多Topic下复制线程也会一直发送空请求
四、Topic多资源占用大
经过逐一分析、排除与测试,主要缘由仍是在第三点:服务端在复制处理在Topic数量多的状况下变慢致使的。
例如10个Topic的时候,若是用10个复制线程(目前性能测试就是配置10)用于副本复制,则每一个复制线程会分配到1个Topic;而当Topic有600个的时候,若是仍是10个复制线程用于副本复制,则每一个复制线程会分配到60个Topic。 若是此时只发送前10个Topic的时候,颇有可能只有1个复制线程在工做,其余的复制线程因为分配到的Topic没有数据,基本处于空闲状态。
2.解决措施
既然复制线程变慢,咱们能够经过继续增长复制线程的方式提升性能,在600个Topic场景只发送10个Topic场景下,咱们把复制线程提高到60个,这样10个Topic能尽量分配到不一样的复制线程中,提升了复制的速度。如下是实际测试结果:
能够看到增长到60个fetch线程后,时延变为100ms左右。同时原来的环境下,经过增长复制线程(修改配置num.replica.fetchers=60),在原环境下1200个发送线程即便启动SSL,性能也能达到11000+。
性能提高措施总结
RESTful API是同步接口,可是内部使用的SDK接口是异步发送。根据高可靠场景下异步发送的能力能达到2W+ TPS来看,主要仍是同步接口的并发压力上不去致使的,能够经过如下措施来改进:
一、增长请求等待时间linger.ms
经过在客户端增长参数linger.ms,使得每一个请求回等待指定的时间后再发送,使得每一个请求能够发送更多的数据,即增长合包率。
二、增长同步发送对同1个Topic的并发数量
三、减小Topic的分区数
由于目前RESTful API并无用尽服务端的能力(1个分区的能力瓶颈还没达到),默认的3个分区是浪费资源,而且会致使合包率下降,若是采用1个分区,则一样的压力下,合包率能提高3倍,这样性能也能提高。这个措施还能够支持更多的Topic数。
四、增长复制线程
五、考虑提供异步发送SDK接口