Kafka - BufferPool模型

如果请求的内存大于BufferPool中总共可用的内存,就需要额外增加内存,通过Deque的方法传入内存。free list的内存= free.size() * poolableSize, free list 的内存 + nonPooledAvailableMemory = 总共可用的内存。

 

先来看下它的属性:

static final String WAIT_TIME_SENSOR_NAME = "bufferpool-wait-time";

private final long totalMemory;
private final int poolableSize;
private final ReentrantLock lock;
private final Deque free;
private final Deque waiters;
/** Total available memory is the sum of nonPooledAvailableMemory and the number of byte buffers in free * poolableSize.  */
private long nonPooledAvailableMemory;
private final Metrics metrics;
private final Time time;
private final Sensor waitTime;

再来看下它的构造器:

/**
 * 创建一个缓冲池
 *
 * @param memory 这个缓冲池最大能调用的内存大小
 * @param poolableSize 缓冲池能复用的内存大小
 * @param metrics 测量实例
 * @param time 时间实例
 * @param metricGrpName 测量实例组名
 */
public BufferPool(long memory, int poolableSize, Metrics metrics, Time time, String metricGrpName) {
    this.poolableSize = poolableSize;
    this.lock = new ReentrantLock();
    this.free = new ArrayDeque<>();
    this.waiters = new ArrayDeque<>();
    this.totalMemory = memory;
    this.nonPooledAvailableMemory = memory;
    this.metrics = metrics;
    this.time = time;
    this.waitTime = this.metrics.sensor(WAIT_TIME_SENSOR_NAME);
    MetricName rateMetricName = metrics.metricName("bufferpool-wait-ratio",
                                               metricGrpName,
                                               "The fraction of time an appender waits for space allocation.");
    MetricName totalMetricName = metrics.metricName("bufferpool-wait-time-total",
                                               metricGrpName,
                                               "The total time an appender waits for space allocation.");
    this.waitTime.add(new Meter(TimeUnit.NANOSECONDS, rateMetricName, totalMetricName));
}

接着是本文要讲解的核心方法:

allocate方法的作用:分配指定大小的内存空间给buffer。如果没有足够的内存、缓冲池设置了阻塞模式,这个方法将会被阻塞。

方法的参数:

            size :分配的内存大小,maxTimeToBlocksMs:最大阻塞的时长。 

    public ByteBuffer allocate(int size, long maxTimeToBlockMs) throws InterruptedException {
        // 因为设置了最大可调用的内存,所以说每次分配的内存不能大于它
        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;

        // 考虑到线程安全性问题,用可重入锁加锁
        this.lock.lock();
        try {
            // check if we have a free buffer of the right size pooled
            // 如果指定的内存大小等于可重用的内存大小并且双端队列不是空的
            // 就取出队列的头部中存储的元素,进行返回。
            // 这里的队列充当着缓存的作用
            if (size == poolableSize && !this.free.isEmpty())
                return this.free.pollFirst();

            // now check if the request is immediately satisfiable with the
            // memory on hand or if we need to block
            // 因为队列里每个元素都存储着ByteBuffer,计算总共可重用的内存大小
            // 就要用队列的size乘队列中每个元素的ByteBuffer可重用的内存大小
            int freeListSize = freeSize() * this.poolableSize;
            
            // 非可重用的内存+可重用的内存 >= 指定分配的内存
            if (this.nonPooledAvailableMemory + freeListSize >= size) {
                // we have enough unallocated or pooled memory to immediately
                // satisfy the request, but need to allocate the buffer
                freeUp(size);
                this.nonPooledAvailableMemory -= size;
            // 内存不够用,进行阻塞
            } else {
                // we are out of memory and will have to block
                int accumulated = 0;
                Condition moreMemory = this.lock.newCondition();
                try {
                    // 剩余的阻塞时长
                    long remainingTimeToBlockNs = TimeUnit.MILLISECONDS.toNanos(maxTimeToBlockMs);
                    this.waiters.addLast(moreMemory);
                    // loop over and over until we have a buffer or have reserved
                    // enough memory to allocate one
                    while (accumulated < size) {
                        long startWaitNs = time.nanoseconds();
                        long timeNs;
                        boolean waitingTimeElapsed;
                        try {
                            // 阻塞
                            waitingTimeElapsed = !moreMemory.await(remainingTimeToBlockNs, TimeUnit.NANOSECONDS);
                        } finally {
                            long endWaitNs = time.nanoseconds();
                            // 本次阻塞花费的时长
                            timeNs = Math.max(0L, endWaitNs - startWaitNs);
                            this.waitTime.record(timeNs, time.milliseconds());
                        }

                        if (waitingTimeElapsed) {
                            throw new TimeoutException("Failed to allocate memory within the configured max blocking time " + maxTimeToBlockMs + " ms.");
                        }
                        // 剩余的阻塞时长
                        remainingTimeToBlockNs -= timeNs;

                        // check if we can satisfy this request from the free list,
                        // otherwise allocate memory
                        // 非可重用内存不够,再去考虑可重用的内存。如果accumulted等于0
                        // 并且需要调用的大小size等于可重用内存的大小并且deque不空
                        if (accumulated == 0 && size == this.poolableSize && !this.free.isEmpty()) {
                            // just grab a buffer from the free list
                            // 从双端队列的头部获取ByteBuffer
                            buffer = this.free.pollFirst();
                            // 设置收集到的大小为size
                            accumulated = size;
                        // 可重用内存也无法满足请求,需要额外分配内存,这些内存不可重用,使用完
                        // 直接gc
                        } else {
                            // we'll need to allocate memory, but we may only get
                            // part of what we need on this iteration
                            // 还需要分配size - accumulated大小的内存
                            // 因为本次操作收集到了accumulated大小的内存 
                            // 也就是为非可重用的内存分配size - accumulated大小的内存
                            freeUp(size - accumulated);
                            // 此时的非可重用内存使用的内存大小
                            // 有可能现在增加了额外的内存之后的非可重用内存还不够用
                            // 这是就是那句使用部分的内存,分配给非可重用内存一定内存后,即使
                            // 还不够用,也只使用这部分的内存完成请求
                            int got = (int) Math.min(size - accumulated, this.nonPooledAvailableMemory);
                            // 现在非可重用的内存剩余内存
                            this.nonPooledAvailableMemory -= got;
                            // 收集到的内存
                            accumulated += got;
                        }
                    }
                    // Don't reclaim memory on throwable since nothing was thrown
                    // 遇到异常,这里没有抛出,只设置accumulated等于0,回到原点
                    accumulated = 0;
                } finally {
                    // When this loop was not able to successfully terminate don't loose available memory
                    // 不释放占用的内存,而是放回到非可重用内存
                    this.nonPooledAvailableMemory += accumulated;
                    this.waiters.remove(moreMemory);
                }
            }
        } finally {
            // signal any additional waiters if there is more memory left
            // over for them
            try {
                // 还有空闲空间,唤醒下一个线程
                if (!(this.nonPooledAvailableMemory == 0 && this.free.isEmpty()) && !this.waiters.isEmpty())
                    this.waiters.peekFirst().signal();
            } finally {
                // Another finally... otherwise find bugs complains
                lock.unlock();
            }
        }

        if (buffer == null)
            // 如果调用内存失败,交给下一个线程执行任务
            return safeAllocateByteBuffer(size);
        else
            return buffer;
    }

1.1 freeUp方法:确保有至少指定请求大小的内存,可以通过释放可重用的内存的方式。 

    private void freeUp(int size) {
        while (!this.free.isEmpty() && this.nonPooledAvailableMemory < size)
            this.nonPooledAvailableMemory += this.free.pollLast().capacity();
    }

最后这个方法是释放内存: 

    public void deallocate(ByteBuffer buffer, int size) {
        lock.lock();
        try {
            // 如果使用的完全是可重用的内存,对buffer执行clear操作(nio操作),加入到free list
            if (size == this.poolableSize && size == buffer.capacity()) {
                buffer.clear();
                this.free.add(buffer);
            } else {
                // 还给非可重用的内存
                this.nonPooledAvailableMemory += size;
            }
            // 唤醒阻塞的线程
            Condition moreMem = this.waiters.peekFirst();
            if (moreMem != null)
                moreMem.signal();
        } finally {
            lock.unlock();
        }
    }

 

你可能感兴趣的:(Kafka,Kafka)