一、RecordAccmulator分析
注意,当前使用的kafka版本是0.10.1.0。 在kafkaProducer发送消息的时候,其实它并不是直接和kafka server进行通信,而是将消息暂存在一个缓冲区里面,主线程通过send方法发送后就可以立即返回而无需阻塞,业务线程不断通过KafkaProducer.send方法将消息发送到RecordAcccmulator里面,当达到一定的限制条件,会唤醒后台Sender线程从缓冲区取消息和kafkaServer进行通信。
下面我们首先来分析一个这个RecordAccmulator的结构,先看一些属性
// RecordAccumulator是否关闭的标志位
private volatile boolean closed;
// 索引号
private int drainIndex;
// flushes过程计数器
private final AtomicInteger flushesInProgress;
// appends过程计数器
private final AtomicInteger appendsInProgress;
// 批量大小
private final int batchSize;
// 压缩器类型
private final CompressionType compression;
// 延迟时间
private final long lingerMs;
// 重试时间
private final long retryBackoffMs;
// 缓冲池
private final BufferPool free;
private final Time time;
// TopicPartition到RecordBatch双端队列的ConcurrentMap集合batches
private final ConcurrentMap<TopicPartition, Deque<RecordBatch>> batches;
// 处于完成状态的批量记录
private final IncompleteRecordBatches incomplete;
这里比较重要的属性是batches,其他的都是一些统计信息,可以忽略不计,不影响对它的整体理解,通过batches属性,可以看到,他是一个concurrentMap,也就是它是线程安全的,map的key是具体的分区信息,value是这个分区对应的双端队列,队列里面才是放的具体消息,所以接下来我们先看RecordBatch,同样,先看一下它的成员变量
// 记录数目
public int recordCount = 0;
// 最大记录大小
public int maxRecordSize = 0;
// 尝试次数
public volatile int attempts = 0;
// RecordBatch创建时间
public final long createdMs;
public long drainedMs;
// 上次尝试时间
public long lastAttemptMs;
// 内存记录MemoryRecords,在内存中存储Record
public final MemoryRecords records;
// 分区具体信息
public final TopicPartition topicPartition;
// 请求结果实例produceFuture
public final ProduceRequestResult produceFuture;
// 上次添加记录时间
public long lastAppendTime;
// 回调函数结构体列表thunks
private final List<Thunk> thunks;
// 重试标志位
private boolean retry;
而这个里面最重要的当然是MemoryRecords,这才是真正存放消息的地方,所以我们倒过来看,先看MemoryRecords
// the compress