Kafka的NIO解析
目录
Kafka生产者源码解析之一KafkaProducer
Kafka生产者源码解析之二RecordAccumulator
Kafka生产者源码解析之三NIO
Kafka生产者源码解析之四Sender
Kafka生产者源码解析之五小结
回顾
之前两篇介绍了KafkaProducer类send消息的具体过程,和RecordAccumulator类append消息的具体过程。这一条线路其实就算完了,可是总觉得还缺点什么。
RecordAccumulator.RecordAppendResult result = accumulator.append(tp, timestamp, serializedKey,
serializedValue, headers, interceptCallback, remainingWaitMs);
if (result.batchIsFull || result.newBatchCreated) {
log.trace("Waking up the sender since topic {} partition {} is either full or getting a new batch", record.topic(), partition);
this.sender.wakeup();
}
return result.future;
由上面代码可知append消息之后,唤醒了Sender线程,这就是第二条线路,所以接下来我们就来对此类一探究竟,因为似乎到现在我们也不知道消息最终去了哪里。只知道消息被写入到MemoryRecordsBuilder对象里的appendStream属性里了,这个属性是一个数据输出流。可是这跟NIO又有什么关系呢,别急,往下看。
线索
目前还不会画图,所以只能一步一步来说了,先大致说下流程,下面再详细介绍每一步的由来。
- KafkaProducer.sender 其实就是 Sender类,即调用了此类的wakeup方法。
- Sender.wakeup方法里面调用了 this.client.wakeup() ,client是NetworkClient类。
- NetworkClient.wakeup方法里调用了 this.selector.wakeup(); selector 其实是 org.apache.kafka.common.network 包里面的Selector类。
- Selector.wakeup方法里调用了 this.nioSelector.wakeup(); nioSelector 其实就是 java.nio.channels.Selector 类,即 NIO原生的选择器类,它是一个抽象类。windows系统的话这里是 WindowsSelectorImpl类
- WindowsSelectorImpl.wakeup方法就是唤醒选择器的作用,其底层也是用的本地方法实现。
NIO核心组件之一Selector
对NIO不清楚的可以自学,毕竟是java源生的架构,网上资料很多,这里只单纯的结合kafka介绍NIO的用法,主要目的是更全面的认识Kafka
通过上面的线索可以知道,当我们用kafka的生产者发送消息的时候,除了消息会被追加到RecordAccumulator这个对象里之外,还会调用一个Sender的wakeup方法。
// 不清楚的同学可以看上两篇博客
RecordAccumulator.RecordAppendResult result = accumulator.append(tp, timestamp, serializedKey,
serializedValue, headers, interceptCallback, remainingWaitMs);
if (result.batchIsFull || result.newBatchCreated) {
log.trace("Waking up the sender since topic {} partition {} is either full or getting a new batch", record.topic(), partition);
this.sender.wakeup();
}
此处的sender到底是什么呢,我们看下KafkaProducer 的构造器,里面通过configs初始化了很对对象,所以我只截取初始化 sender的部分。
this.errors = this.metrics.sensor("errors");
this.sender = newSender(logContext, kafkaClient, this.metadata);
String ioThreadName = NETWORK_THREAD_PREFIX + " | " + clientId;
...
// 初始化sender方法
Sender newSender(LogContext logContext, KafkaClient kafkaClient, Metadata metadata) {
int maxInflightRequests = configureInflightRequests(producerConfig, transactionManager != null);
int requestTimeoutMs = producerConfig.getInt(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG);
ChannelBuilder channelBuilder = ClientUtils.createChannelBuilder(producerConfig);
ProducerMetrics metricsRegistry = new ProducerMetrics(this.metrics);
Sensor throttleTimeSensor = Sender.throttleTimeSensor(metricsRegistry.senderMetrics);
// 我们创建kafkaproducer得时候只传了一个配置文件对象,所以这里new了一个NetworkClient对象
KafkaClient client = kafkaClient != null ? kafkaClient : new NetworkClient(
// 创建一个org.apache.kafka.common.network.Selector对象
new Selector(producerConfig.getLong(ProducerConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG),
this.metrics, time, "producer", channelBuilder, logContext),
metadata,
clientId,
maxInflightRequests,
producerConfig.getLong(ProducerConfig.RECONNECT_BACKOFF_MS_CONFIG),
producerConfig.getLong(ProducerConfig.RECONNECT_BACKOFF_MAX_MS_CONFIG),
producerConfig.getInt(ProducerConfig.SEND_BUFFER_CONFIG),
producerConfig.getInt(ProducerConfig.RECEIVE_BUFFER_CONFIG),
requestTimeoutMs,
ClientDnsLookup.forConfig(producerConfig.getString(ProducerConfig.CLIENT_DNS_LOOKUP_CONFIG)),
time,
true,
apiVersions,
throttleTimeSensor,
logContext);
int retries = configureRetries(producerConfig, transactionManager != null, log);
short acks = configureAcks(producerConfig, transactionManager != null, log);
// 返回Sender对象,这里得client就是上面new得NetworkClient对象
return new Sender(logContext,
client,
metadata,
this.accumulator,
maxInflightRequests == 1,
producerConfig.getInt(ProducerConfig.MAX_REQUEST_SIZE_CONFIG),
acks,
retries,
metricsRegistry.senderMetrics,
time,
requestTimeoutMs,
producerConfig.getLong(ProducerConfig.RETRY_BACKOFF_MS_CONFIG),
this.transactionManager,
apiVersions);
}
由上面代码可知,
Sender.client = NetworkClient
NetworkClient.selecto r= org.apache.kafka.common.network.Selector
再看下方法得调用关系。
// Sender类
publ