一、问题现象:
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:创作不易,如果解决了您的问题,请点赞、收藏、关注三连。如未能解决问题,欢迎私信我一起讨论交流。