文章目录
1. Why BufferPool ?
Kafka Producer以ProducerBatch为单位发送数据,而ProducerBatch中的数据以ByteBuffer的形式进行存储。当发送端数据量极大时,ByteBuffer就会无限制地频繁申请,可能会引发OOM;另外,发送完数据后,ByteBuffer就会释放,会频繁的引发FullGC,影响Kafka性能。
为此,设计了Kafka Producer端的内存池,它具有以下功能:
- 限制可以申请的内存总量totalMemory,防止OOM,totalMemory可以通过KafkaProducer配置项指定;
- free池持有ProducerRecord的引用,减少FullGC的频率;
KafkaProducer发送流程:

KafkaProducer相关数据结构:

1.1 Why two kinds of pools ?
为什么需要free池?
new一个对象时,需要经历申请对象空间、设置引用(引用常量池中的常量,引用堆中的对象)等过程,代价较大。而如果能够直接从一个容器中取出已经实例化好的对象,则可以省去以上步骤,避免频繁的实例化。而要想将ByteBuffer都实例化好,则必然需要给ByteBuffer设定一个大小即poolableSize。
为什么需要availableMemory池?
由于free池中的ByteBuffer对象都是固定大小的,而KafkaProducer端发送的数据未必都能被ByteBuffer装下,因此遇到size > poolableSize的时候,我们需要通过availableMemory池来申请ByteBuffer对象。
2. What is a BufferPool ?
名词解释:
free:该池子中存储大小等于poolableSize的ByteBuffer;
availableMemory:内存池中,除了free池和已申请的ByteBuffer,剩余的字节大小。物理上,其处于JVM堆内存中,只是通过nonPooledAvailableMemory标记来约束其可以从堆内存申请的字节大小;
totalMemory:内存池可以申请的ByteBuffer字节总大小;
poolableSize:free池中ByteBuffer的固定大小;

3. How BufferPool run ?
步骤:
- 如果申请的ByteBuffer size超过totalMemory,抛异常;
- 如果申请的size符合预设的poolableSize,则从free池获取;
- 如果申请的size不符合预设的poolableSize,但是free池和availableMemory池的总大小可以满足
- 尝试从availableMemory池直接申请;
- 如果availableMemory池容量小于size,释放free池中的ByteBuffer,添加到availableMemory池;(nonPooledAvailableMemory + = poolableSize),然后再从availableMemory池直接申请;
- free池和availableMemory池的总大小不足矣满足size大小,则等待已申请的ByteBuffer释放,ProducerRecord在成功发送到broker后(参考
Sender#handleProduceResponse),会进行ByteBuffer的释放。释放的字节大小会重新回到free池或availableMemory池,释放的字节大小通过accumulated计数器进行技术,当accumulated >= size,再进行申请;
流程图:

源码:
public class BufferPool {
static final String WAIT_TIME_SENSOR_NAME = "bufferpool-wait-time";
// 内存池最多可以申请的字节大小
private final long totalMemory;
// free池中单个ByteBuffer的大小
private final int poolableSize;
// free池用一个双端队列来持有,因此其中的ByteBuffer不会被GC
private final Deque<ByteBuffer> free;
// 用于等待已申请的ByteBuffer释放的条件锁
private final Deque<Condition> waiters;
// 用于标记AvailableMemory的大小
private long nonPooledAvailableMemory;
public ByteBuffer allocate(int size, long maxTimeToBlockMs) throws InterruptedException {
// 申请的size大于内存池总大小,抛异常,可以通过KafkaProducer进行重新设置
if (size > this.totalMemory)
throw new IllegalArgumentException("Attempt to allocate " + size
+ " bytes, but there is a hard limit of "
+ this.totalMemory
+ " on memory allocations.");
ByteBuffer buffer = null;

最低0.47元/天 解锁文章
296

被折叠的 条评论
为什么被折叠?



