Kafka源码分析之MemoryRecords

来源:互联网 发布:samba for linux 编辑:程序博客网 时间:2024/05/13 17:56

        MemoryRecords是Kakfa中Record在内存中的实现形式,它基于Java NIO中ByteBuffer来实现。MemoryRecords中成员变量如下:

    private final static int WRITE_LIMIT_FOR_READABLE_ONLY = -1;    // the compressor used for appends-only    // 仅仅用于appends的压缩器Compressor实例compressor    private final Compressor compressor;    // the write limit for writable buffer, which may be smaller than the buffer capacity    // 可写缓冲区的写限制writeLimit,可能小于缓存区的装载能力    private final int writeLimit;    // the capacity of the initial buffer, which is only used for de-allocation of writable records    // 最初的缓冲区的装载能力initialCapacity,仅用于重新分配可写的记录    private final int initialCapacity;    // the underlying buffer used for read; while the records are still writable it is null    // 用于读的底层缓冲区buffer,java NIO的ByteBuffer类,此时记录仍可写    private ByteBuffer buffer;    // indicate if the memory records is writable or not (i.e. used for appends or read-only)    // 标志内存记录是否可写的状态位writable    private boolean writable;
        其中,compressor是仅仅用于appends的压缩器Compressor实例,writeLimit是可写缓冲区的写限制,它可能小于缓存区的装载能力,initialCapacity是最初的缓冲区的装载能力,仅用于重新分配可写的记录,buffer是java NIO的ByteBuffer实例,用于读的底层缓冲区,而writable是一个标志内存记录是否可写的状态位。

        再来看下MemoryRecords的构造函数,如下:

    // Construct a writable memory records    private MemoryRecords(ByteBuffer buffer, CompressionType type, boolean writable, int writeLimit) {            // 根据入参赋值writable、writeLimit    this.writable = writable;        this.writeLimit = writeLimit;                // initialCapacity取Java NIO中ByteBuffer的capacity        this.initialCapacity = buffer.capacity();                if (this.writable) {// 如果writable为true,即处于可写状态                // buffer设置为null            this.buffer = null;                        // 利用入参ByteBuffer类型的buffer和CompressionType类型的type构造Compressor实例compressor            this.compressor = new Compressor(buffer, type);        } else {                // buffer设置为入参ByteBuffer类型的buffer            this.buffer = buffer;                        // compressor设置为null            this.compressor = null;        }    }
        通过MemoryRecords的构造函数我们可以知道,MemoryRecords有两种基本的状态,一个是只写,一个是只读,当为只写时,标志位writable为true,此时MemoryRecords中ByteBuffer类型的成员变量buffer被设置为null,同时利用入参ByteBuffer类型的buffer和CompressionType类型的type构造Compressor实例compressor;当为只读时,buffer设置为入参ByteBuffer类型的buffer,compressor设置为null。

        MemoryRecords最主要的一个功能就是添加记录,而实现这一功能的方法就是append()方法,代码如下:

    /**     * Append a new record and offset to the buffer     * 添加一条新的记录,并且在缓冲区buffer中记录偏移量offset     */    public void append(long offset, byte[] key, byte[] value) {        // 首先判断MemoryRecords的可写标志位writable    if (!writable)            throw new IllegalStateException("Memory records is not writable");    // 根据key、value通过Record的recordSize()方法计算记录大小size        int size = Record.recordSize(key, value);                // 压缩器compressor中记录偏移量offset、记录大小size、记录key和value        compressor.putLong(offset);        compressor.putInt(size);        compressor.putRecord(key, value);                // 压缩器compressor中通过recordWritten()方法累加记录数numRecords和已写入未压缩总大小writtenUncompressed,        // 记录数numRecords为加1,        // 而已写入未压缩总大小writtenUncompresse是记录大小size再加上size所占大小和offset所占大小之和LOG_OVERHEAD,也就是额外的Int+Long为12        compressor.recordWritten(size + Records.LOG_OVERHEAD);    }
        上面为key、value形式的append,而还有一种Record形式的append,代码如下:

    /**     * Append the given record and offset to the buffer     */    public void append(long offset, Record record) {            // 首先判断MemoryRecords的可写标志位writable    if (!writable)            throw new IllegalStateException("Memory records is not writable");    // 获取记录大小size        int size = record.size();                // 压缩器compressor中记录偏移量offset、记录大小size、记录record的buffer        compressor.putLong(offset);        compressor.putInt(size);        compressor.put(record.buffer());                // 压缩器compressor中通过recordWritten()方法累加记录数numRecords和已写入未压缩总大小writtenUncompressed,        // 记录数numRecords为加1,        // 而已写入未压缩总大小writtenUncompresse是记录大小size再加上size所占大小和offset所占大小之和LOG_OVERHEAD,也就是额外的Int+Long为12        compressor.recordWritten(size + Records.LOG_OVERHEAD);                // 重绕此缓冲区,将位置设置为零并丢弃标记        record.buffer().rewind();    }
        MemoryRecords中还有一个针对指定Record的key、value来判断是否尚有余地的hasRoomFor()方法,代码如下:

    /**     * Check if we have room for a new record containing the given key/value pair     *     * Note that the return value is based on the estimate of the bytes written to the compressor, which may not be     * accurate if compression is really used. When this happens, the following append may cause dynamic buffer     * re-allocation in the underlying byte buffer stream.     *     * There is an exceptional case when appending a single message whose size is larger than the batch size, the     * capacity will be the message size which is larger than the write limit, i.e. the batch size. In this case     * the checking should be based on the capacity of the initialized buffer rather than the write limit in order     * to accept this single record.     */    public boolean hasRoomFor(byte[] key, byte[] value) {        return this.writable && this.compressor.numRecordsWritten() == 0 ?            this.initialCapacity >= Records.LOG_OVERHEAD + Record.recordSize(key, value) :            this.writeLimit >= this.compressor.estimatedBytesWritten() + Records.LOG_OVERHEAD + Record.recordSize(key, value);    }
        hasRoomFor()方法中,需要首先判断writable是否为true,writable为false的话会直接返回false,直接通知调用者MemoryRecords已无余地存储Record。writable为true的话,还需要判断compressor的已写入数据大小numRecordsWritten是否为0,为0 的话,根据MemoryRecords的最初的缓冲区的装载能力initialCapacity是否大于key、value、offset所占大小、size所占大小之和来确定是否尚有足够空间容纳一个Record,不为0 的话,则根据可写缓冲区的写限制writeLimit是否大于compressor预估算的大小与key、value、offset所占大小、size所占大小之和来确定是否尚有足够空间容纳一个Record。compressor预估算的大小通过如下方法来判断:

    public long estimatedBytesWritten() {        if (type == CompressionType.NONE) {            return bufferStream.buffer().position();        } else {            // estimate the written bytes to the underlying byte buffer based on uncompressed written bytes            return (long) (writtenUncompressed * TYPE_TO_RATE[type.id] * COMPRESSION_RATE_ESTIMATION_FACTOR);        }    }
        







2 0