Kafka 新版Producer Java版代码阅读
Kafka在0.8.2.1出了新版Producer,支持ack(仅Java版,因为通过JavaClient实现的)。因此对代码进行了简单阅读,并记录如下:
接口如下:
public Future<RecordMetadata> send(ProducerRecord<K,V> record, Callback callback)
封装一个Record之后,每次调用同时传入一个callback。该函数在Kafka返回结果时被调用。
根据官方example的调用方式:
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (metadata != null) {
//means success
} else {
//means fail
exception.printStackTrace();
}
}
1. 如果我来做,怎么做?
我觉得如果我来设计,至少需要考虑如下几个问题:
发送的时候callback是否跟着发到Kafka Server?
Kafka支持了batch send,ack的时候是一个一个ack还是batch ack?同样,如果是batch ack,call back怎么调用?
每次callback都会单独使用一个线程调用么?还是共享一个线程?
如果Callback不发送到KafkaServer,在客户端是怎样存储的?进程fail掉的时候是否会丢ack?
======================
2. 带着这几个问题,我们来看人家怎么做的
======================
2.1 基本逻辑
先从Kafka Producer的send方法看起,
send的全部代码就是这样,简单来说做了这样几件事
- 判断partition
- 序列化消息
- 判断消息大小是否符合格式
重点是第四步:
RecordAccumulator.RecordAppendResult result = accumulator.append(tp, serializedKey, serializedValue, compressionType, callback);
accumulator 是每个Producer单独持有唯一一个的,每次调用appen之后返会一个包含有(FutureRecordMetadata)的执行result.
追进去看一下这个append方法,注释是这样说的
Add a record to the accumulator, return the append result。The append result will contain the future metadata, and flag for whether the appended batch is full or a new batch is created.
简单来说这个方法就是把一个message序列化之后加入到accumulator的发送队列里,等会再详细介绍Acculator。
第五步,调用sender的awake方法
if (result.batchIsFull || result.newBatchCreated) { this.sender.wakeup(); }
看到这里感觉啥都没干啊,所以我们需要进一步看一下Acculator以及Sender究竟在做什么。
======================
2.2 Accumulator
在Producer里通过Accumulator的append方法把消息加入异步发送队列,我们先看看Accumulator的实现。
private final BufferPool free;
private final ConcurrentMap<TopicPartition, Deque<RecordBatch>> batches;
private final IncompleteRecordBatches incomplete;
Accumulator里有三个结构必须要说一下,BufferPool用于管理batch发送的缓存,等会细说,batche