一、问题现象:

       kafka发送producer为单实例(使用new kafkaProducer)并且使用同步发送,发送kafka使用线程池执行发送任务,任务队列大小为2000,kafka连接server端使用了kerboeros认证系统。
       当业务下发从nginx服务器进入,两个tomcat节点来完成发送kafka处理,超过一千TPS时会出现几次处理时延很长,直接导致nginx504。

       第一时间打开debug日志,发现最长处理kafka发送时延也就1秒多,但是我nginx配置超时时间为60s,怎么会导致nginx超时呢?

二、问题分析:

       分析kafka日志发现,每次发送数据大部分时间在0-1ms,出现时延的情况时几乎都是连续出现的。

       由于发送端只有一个producer实例,这样当一个message发送阻塞了,将会瞬间导致TPS急剧下降。

       正常情况下一个kafka实例在1秒内能够处理1000个发送请求,但出现1秒的时延将会导致1秒只能处理1个发送请求,这样会阻塞后面数据的处理,马上将线程池任务队列塞满,后面的nginx请求也就随之阻塞,当nginx在60s后没有收到任何相应就504的问题

 

三、问题原因:

       由于producer是线程安全的,所以采用单实例,但一次发送阻塞(因为使用同步发送,每次发送都会等待结果,这个过程是同步的),这肯定会影响到后续的数据处理。

四、修改策略(问题解决):

       将producer改为多实例,使用数组将多个实例缓存起来,每次使用producer是在数组轮询取出一个进行使用,修改之后连续长稳环境跑了24小时。

       打开日志发现发送kafka时延还是有少量100ms以上的时延,但基本都在100ms-500ms之间,并且当不会连续出现了,nginx也没有出现504这种情况了,说明多实例修改是有效果的。

PS:如果上述修改依然没作用,那么请查看节点映射,即查看系统中的hosts文件,节点地址和节点名称映射是否唯一

KafkaSpoutConfig.builder("node01:9092,node02:9092,node03:9092", "test22");中的节点映射,即查看系统中的hosts文件,节点地址和节点名称映射是否唯一。

 

五、几个Kafka的调优配置:

1)当然最简单就是增加broker节点,增加分区数量,提高并行度,

       除此以外,还有以下一些方法,概括为静态的和动态的配置:

 

2)静态配置:

GC相关:

       Java 7引进了G1 垃圾回收,使得GC调优变的没那么难。G1主要有两个配置选项来调优:MaxGCPauseMillis 和 InitiatingHeapOccupancyPercent. Kafka的启动脚本不支持G1算法,需要在环境变量中设置:

       export KAFKA_HEAP_OPTS="-Xmx8G -Xms8G -Xmn4G -XX:PermSize=64m -XX:MaxPermSize=128m  -XX:SurvivorRatio=6  -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly"

 

OS相关:

1. 因为Kafka数据持久化到硬盘,所以理论上增加磁盘数量,可以增加吞吐量;

       server.properties 中的log.dirs来配置多个硬盘,多个存储路径;

2. 增大socket buffer size:

       Enabling High Performance Data Transfers

       默认值,可以改成4倍大小。

# cat /proc/sys/net/core/rmem_max 

124928

# cat /proc/sys/net/core/wmem_max 

124928

3. 文件描述符:Kafka会使用大量文件和网络socket,所以,我们需要把file descriptors的默认配置改为100000。

#vi /etc/sysctl.conf

fs.file-max = 32000


#vi /etc/security/limits.conf

yourusersoftnofile10000

youruserhardnofile30000

3)动态配置:

       unclean.leader.election.enable:不严格的leader选举,有助于集群健壮,但是存在数据丢失风险。

       max.message.bytes:单条消息的最大长度。如果修改了该值,那么replica.fetch.max.bytes和消费者的fetch.message.max.bytes也要跟着修改。

       cleanup.policy:生命周期终结数据的处理,默认删除。

       flush.messages:强制刷新写入的最大缓存消息数。

       flust.ms:强制刷新写入的最大等待时长。

 

       min.insync.replicas:如果同步状态的副本小于该值,服务器将不再接受request.required.acks为-1或all的写入请求。

▪一些典型的值: 
0: 表示Producer从来不等待来自broker的确认信息。这个选择提供了最小的时延但同时风险最大(因为当server宕机时,数据将会丢失)。 
1:表示获得Leader replica已经接收了数据的确认信息。这个选择时延较小同时确保了server确认接收成功。 
-1:Producer会获得所有同步replicas都收到数据的确认。同时时延最大,然而,这种方式并没有完全消除丢失消息的风险,因为同步replicas的数量可能是1。如果你想确保某些replicas接收到数据,那么你应该在Topic-level设置中选项min.insync.replicas设置一下。

 

 

PS:创作不易,如果解决了您的问题,请点赞、收藏、关注三连。如未能解决问题,欢迎私信我一起讨论交流。