kafka发送客户端在高并发场景下如何保证不频繁GC的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。
10年积累的网站制作、成都做网站经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计后付款的网站建设流程,更有乌拉特后免费网站建设让你可以放心的选择与我们合作。
最近看kafka源码,着实被它的客户端缓冲池技术优雅到了。
注:用到的源码来自kafka2.2.2版本。
背景
当我们应用程序调用kafka客户端 producer发送消息的时候,在kafka客户端内部,会把属于同一个topic分区的消息先汇总起来,形成一个batch。真正发往kafka服务器的消息都是以batch为单位的。如下图所示:
这么做的好处显而易见。客户端和服务端通过网络通信,这样批量发送可以减少网络带来的性能开销,提高吞吐量。
这个Batch的管理就非常值得探讨了。可能有人会说,这不简单吗?用的时候分配一个块内存,发送完了释放不就行了吗。
kafka是用java语言编写的(新版本大部分都是用java实现的了),用上面的方案就是使用的时候new一个空间然后赋值给一个引用,释放的时候把引用置为null等JVM GC处理就可以了。
看起来似乎也没啥问题。但是在并发量比较高的时候就会频繁的进行GC。我们都知道GC的时候有个stop the world
,尽管最新的GC技术这个时间已经非常短,依然有可能成为生产环境的性能瓶颈。
kafka的设计者当然能考虑到这一层。下面我们就来学习下kafka是如何对batch进行管理的。
缓冲池技术原理解析
kafka客户端使用了缓冲池的概念,预先分配好真实的内存块,放在池子里。
每个batch其实都对应了缓冲池中的一个内存空间,发送完消息之后,batch不再使用了,就把内存块归还给缓冲池。
听起来是不是很耳熟啊?不错,数据库连接池,线程池等池化技术其实差不多都是这样的原理。通过池化技术降低创建和销毁带来的开销,提升执行效率。
代码是最好的文档,,下面我们就来撸下源码。
我们撸代码的步骤采用的是从上往下的原则,先带你看看缓冲池在哪里使用,然后再深入到缓存池内部深入分析。
下面的代码做了一些删减,值保留了跟本文相关的部分便于分析。
RecordAccumulator
其实就是管理一个batch队列,我们看到append方法实现其实是调用BufferPool
的free方法申请(allocate
)了一块内存空间(ByteBuffer
), 然后把这个内存空空间包装成batch添加到队列后面。
当消息发送完成不在使用batch的时候,RecordAccumulator
会调用deallocate
方法归还内存,内部其实是调用BufferPool
的deallocate
方法。
很明显,BufferPool
就是缓冲池管理的类,也是我们今天要讨论的重点。我们先来看看分配内存块的方法。
首先整个方法是加锁操作的,所以支持并发分配内存。
逻辑是这样的,当申请的内存大小等于poolableSize
,则从缓存池中获取。这个poolableSize
可以理解成是缓冲池的页大小,作为缓冲池分配的基本单位。从缓存池获取其实就是从ByteBuffer队列取出一个元素返回。
如果申请的内存不等于特定的数值,则向非缓存池申请。同时会从缓冲池中取一些内存并入到非缓冲池中。这个nonPooledAvailableMemory
指的就是非缓冲池的可用内存大小。非缓冲池分配内存,其实就是调用ByteBuffer.allocat
分配真实的JVM内存。
缓存池的内存一般都很少回收。而非缓存池的内存是使用后丢弃,然后等待GC
回收。
继续来看看batch释放的代码,
很简单,也是分为两种情况。要么直接归还缓冲池,要么就是更新非缓冲池部分的可以内存。然后通知等待队列里的第一个元素。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注创新互联行业资讯频道,感谢您对创新互联的支持。
网页题目:kafka发送客户端在高并发场景下如何保证不频繁GC的
URL网址:http://scyingshan.cn/article/iisshc.html