读kafka生产端源码,窥kafka设计之道(下)

背景

在上一篇文章《读kafka生产端源码,窥kafka设计之道(上)》 留下了kafka设计上比较优秀的一个点;内存的循环使用。本篇文章准备盘盘它。

好奇

为什么 kafka减少发送消息时向JVM频繁申请内存,就可以降低JVM GC的执行次数?

我们知道网络上传输的都是二进制数据;而在java中想通过socke网络套接字接口发送数据,底层都是用的ByteBuffer。在往网络上发送数据前,先申请块ByteBuffer的内存;然后把数据写入到此ByteBuffer内存中;调用底层socket的write接口,就OK了;大概伪代码流程

java 复制代码
   //伪代码
   //申请内存
   ByteBuffer buffer = ByteBuffer.allocate(size)
   //内存里加入数据
   buffer.put(XXX)
   //发送数据
   SocketChannel.write(ByteBuffer src)

ByteBuffer占用的内存,什么时候会被回收了?

答:在jvm进行GC时会被回收;

试想如果上面那段代码执行非常频繁,创建ByteBuffer就会很频繁;创建ByteBuffer很频繁,那么申请内存就会很频繁,申请内存越频繁,内存被占满的时间也就会越来越短,内存满了就只能靠不停的GC进行内存的回收,加以重复使用了。而现代JVM里GC的发展目标之一,就是减少GC的停顿时间。GC优化大师从PS,CMS,G1,到ZGC,都在朝这这个方向在努力。

kafka如何解决这个内存频繁申请和GC 频繁释放的问题了?

如果可以用一句简单的话来总结:那么我想应该是 对ByteBuffer的重复使用。 是的用完了不要丢,也不让jvm 给GC了。 即对进行了网络发送的ByteBuffer进行复用;如果有新的消息要发送,可以从缓存池里获取已有 ByteBuffer;然后往里面写入消息数据;当IO线程把ByteBuffer里的消息发往broker并收到对应的响应后,会把ByteBuffer放回缓存池供下一次需要发送的消息循环使用。
大概流程如下图:

核心参数和代码

有两个核心参数,可以控制缓存池BufferPool的行为

  • buffer.memory

    缓存池大小,默认32M。如果IO thread发送消息 的速率比业务线程生产消息 的速度,则会引起业务线程的阻塞,可根据实际情况和jvm大小增大此参数

  • batch.size

    控制每个缓存块ByteBuffer的大小,默认为16K。即一个 BatchRecord里可存的多条消息最大空间

  • ByteBuffer的申请

  • ByteBuffer的回收

总结

如果要编写一款网络应用程序,或者网络框架的工具,我希望能向kafka一样,能考虑到内存的复用;并且减少对上层应用的影响。

假设一个应用通过kafka发送50个G的网络数据;那么kafka的缓存池,就节约了10个G内存的申请和回收;由此减少了多少次GC和GC暂停时间了。那么假设有个50个这样的应用了?总的收益又是多少了?
不是所有的工具都能号称是为应对大数据场景而产生的;kafka做为一款中间件,能比较好的融入大数据生态,kafka的研发人员有自己的独特设计和考虑在支撑这它。

原创不易,请 点赞,关注,留言,转载 4暴击^^

相关推荐
❀always❀20 分钟前
深入浅出分布式限流(更新中)
分布式·wpf
Bug退退退1233 小时前
RabbitMQ 幂等性
分布式·rabbitmq
{⌐■_■}13 小时前
【Kafka】登录日志处理的三次阶梯式优化实践:从同步写入到Kafka多分区批处理
数据库·分布式·mysql·kafka·go
qq_5298353513 小时前
RabbitMQ的消息可靠传输
分布式·rabbitmq
CodeWithMe13 小时前
【Note】《Kafka: The Definitive Guide》 第九章:Kafka 管理与运维实战
运维·分布式·kafka
sql2008help13 小时前
1-Kafka介绍及常见应用场景
分布式·kafka
何苏三月18 小时前
SpringCloud系列 - Seata 分布式事务(六)
分布式·spring·spring cloud
工藤学编程19 小时前
分库分表之实战-sharding-JDBC绑定表配置实战
数据库·分布式·后端·sql·mysql
gtestcandle19 小时前
rabbitmq 的多用户、多vhost使用
分布式·rabbitmq