RecordBatch对象中封装了一个MemoryRecords对象,还有很多控制信息和统计信息。如下:
public final class RecordBatch {
// 保存Recrod的个数
public int recordCount = 0;
// 最大Record的字节数
public int maxRecordSize = 0;
// 记录尝试发送当前RecordBatch的次数。
public volatile int attempts = 0;
public final long createdMs;
// 耗费时间
public long drainedMs;
//最后一次尝试发送的时间戳
public long lastAttemptMs;
// 存储数据的MemoryRecords对象
public final MemoryRecords records;
// 当前 RecordBatch 缓存的消息都会发到这个topicPartition。
public final TopicPartition topicPartition;
// RecordBatch 状态的Future对象
public final ProduceRequestResult produceFuture;
// 最后一次向RecordBatch追加消息的时间戳
public long lastAppendTime;
// Thunk对象集合,可以理解为消息回调对象队列。Thunk中的callback字段就指向对应消息的Callback对象
/**
* A callback and the associated FutureRecordMetadata argument to pass to it.
*********
final private static class Thunk {
final Callback callback;
final FutureRecordMetadata future;
Thunk(Callback callback, FutureRecordMetadata future) {
this.callback = callback;
this.future = future;
}
}
*/
private final List<Thunk> thunks;
// 记录消息在RecordBatch中的偏移量
private long offsetCounter = 0L;
// 是否正在重试
private boolean retry;
}
RecordBatch最重要的方法是添加record到当前结果集,并返回FutureRecord元数据对象
public final class RecordBatch {
public FutureRecordMetadata tryAppend(long timestamp, byte[] key, byte[] value, Callback callback, long now) {
// 估算剩余空间不足,这个并不是一个准确值。
if (!this.records.hasRoomFor(key, value)) {
return null;
} else {
//向MemoryRecord中添加数据,offsetCounter是在RecordBatch中的偏移量。
long checksum = this.records.append(offsetCounter++, timestamp, key, value);
// 更新MemoryRecord中消息中最大的大小和加入时间戳
this.maxRecordSize = Math.max(this.maxRecordSize, Record.recordSize(key, value));
this.lastAppendTime = now;
// FutureRecordMetadata实现了Futher类,在生产者已经收到了某条消息的响应时,
// FutureRecordMetadata.get()会返回RecordMetadata,里面包含了消息的分区和偏移量,给用户callback调用。
FutureRecordMetadata future = new FutureRecordMetadata(this.produceFuture, this.recordCount,
timestamp, checksum,
key == null ? -1 : key.length,
value == null ? -1 : value.length);
// 把用户自定义的callback加入thunk中。
if (callback != null)
thunks.add(new Thunk(callback, future));
this.recordCount++;
return future;
}
}
}
当RecordBatch成功收到响应的时候,正常、超时或者关闭生产者时候,都会调用RecordBatch的done方法,在done方法中回调所有的callback。
public final class RecordBatch {
public void done(long baseOffset, long timestamp, RuntimeException exception) {
log.trace("Produced messages to topic-partition {} with base offset offset {} and error: {}.",
topicPartition,
baseOffset,
exception);
// 顺序执行每个消息的callback
for (int i = 0; i < this.thunks.size(); i++) {
try {
Thunk thunk = this.thunks.get(i);
if (exception == null) {
// 把服务端返回的信息如offset和timestamp,封装成RecordMetadata。
RecordMetadata metadata = new RecordMetadata(this.topicPartition, baseOffset, thunk.future.relativeOffset(),
timestamp == Record.NO_TIMESTAMP ? thunk.future.timestamp() : timestamp,
thunk.future.checksum(),
thunk.future.serializedKeySize(),
thunk.future.serializedValueSize());
//调用消息的自定义callback
thunk.callback.onCompletion(metadata, null);
} else {
//发送失败则直接调用callback,入参为失败异常。
thunk.callback.onCompletion(null, exception);
}
} catch (Exception e) {
log.error("Error executing user-provided callback on message for topic-partition {}:", topicPartition, e);
}
}
//标记整个RecordBatch都处理完成。
this.produceFuture.done(topicPartition, baseOffset, exception);
}
}