RecordBatch是Kafka中Producer中对批量记录的一个封装,它表示正在或将要被发送的一批记录。这个类不是线程安全的,当修改它时必须使用外部同步。RecordBatch中的成员变量如下:
// 记录数目recordCount
public int recordCount = 0;
// 最大记录大小maxRecordSize
public int maxRecordSize = 0;
// 尝试次数attempts
public volatile int attempts = 0;
// RecordBatch创建时间createdMs
public final long createdMs;
public long drainedMs;
// 上次尝试时间lastAttemptMs
public long lastAttemptMs;
// 内存记录MemoryRecords,在内存中存储Record
public final MemoryRecords records;
// 主题和分区的复合体topicPartition
public final TopicPartition topicPartition;
// Produce请求结果ProduceRequestResult实例produceFuture
public final ProduceRequestResult produceFuture;
// 上次添加记录时间lastAppendTime
public long lastAppendTime;
// 回调函数结构体Thunk列表thunks
private final List thunks;
// 重试标志位retry
private boolean retry;
RecordBatch中最主要的一个成员变量是MemoryRecords类型的records,它是Producer发送的记录record在内存中的一个数据集,另外还有一些记录数目recordCount、最大记录大小maxRecordSize、RecordBatch创建时间createdMs、上次尝试时间lastAttemptMs、主题和分区的复合体topicPartition、Produce请求结果ProduceRequestResult实例produceFuture、回调函数结构体列表thunks等重要变量。
Thunk是RecordBatch的一个静态内部类,它是对Produce记录回调函数Callback和其相关参数FutureRecordMetadata的一个封装,其定义如下:
/**
* A callback and the associated FutureRecordMetadata argument to pass to it.
*/
final private static class Thunk {
final Callback callback;
final FutureRecordMetadata future;
public Thunk(Callback callback, FutureRecordMetadata future) {
this.callback = callback;
this.future = future;
}
}
再看它的构造函数,如下:
// 构造函数
public RecordBatch(TopicPartition tp, MemoryRecords records, long now) {
// RecordBatch创建时间createdMs赋值为now
this.createdMs = now;
// 上次尝试时间lastAttemptMs赋值为now
this.lastAttemptMs = now;
// 记录records赋值为records
this.records = records;
// 主题分区topicPartition赋值为tp
this.topicPartition = tp;
// 构造Produce请求结果ProduceRequestResult实例produceFuture
this.produceFuture = new ProduceRequestResult();
// 构造回调函数结构体列表thunks
this.thunks = new ArrayList();
// 上次添加记录时间lastAppendTime赋值为创建时间createdMs,也就是now
this.lastAppendTime = createdMs;
// 重试标志位retry默认为false
this.retry = false;
}
既然为批量记录,那么RecordBatch的最主要一个功能就是添加记录,而记录又不可能无限制添加,所以tryAppend()方法就是完成这个功能的。它尝试添加记录,如果添加成功,则返回FutureRecordMetadata实例,其中包含了记录在记录集中的相对偏移量,否则返回null,代码如下:
/**
* Append the record to the current record set and return the relative offset within that record set
* 添加记录到当前记录集合,返回记录在记录集中的相对偏移量
*
* @return The RecordSend corresponding to this record or null if there isn't sufficient room.
*/
public FutureRecordMetadata tryAppend(byte[] key, byte[] value, Callback callback, long now) {
if (!this.records.hasRoomFor(key, value)) {// 如果内存记录MemoryRecords实例records没有余地
// 直接返回null
return null;
} else {
// 将key、value添加进内存记录MemoryRecords实例records中
this.records.append(0L, key, value);
// 如果需要,更新最大记录大小maxRecordSize
this.maxRecordSize = Math.max(this.maxRecordSize, Record.recordSize(key, value));
// 上次添加记录时间lastAppendTime赋值为now
this.lastAppendTime = now;
// 构造FutureRecordMetadata实例future
FutureRecordMetadata future = new FutureRecordMetadata(this.produceFuture, this.recordCount);
// 将回调函数callback构造成Thunk对象添加到thunks列表
if (callback != null)
thunks.add(new Thunk(callback, future));
// 记录数目recordCount加1
this.recordCount++;
// 返回FutureRecordMetadata实例future
return future;
}
}
tryAppend()方法需要四个参数,键key、值value、回调函数callback、添加时间now,其处理逻辑如下:
1、首先判断内存记录MemoryRecords实例records中有没有余地存储该key、value,如果没有,直接返回null,否则继续;
2、调用MemoryRecords的append()方法,将key、value添加进内存记录MemoryRecords实例records中;
3、如果key、value对应大小超过当前maxRecordSize,更新最大记录大小maxRecordSize;
4、更新上次添加记录时间lastAppendTime为now;
5、根据produceFuture、recordCount构造FutureRecordMetadata实例future;
6、利用future将回调函数callback构造成Thunk对象添加到thunks列表;
7、记录数目recordCount加1;
8、返回FutureRecordMetadata实例future。