Kafka的Sender解析
目录
Kafka生产者源码解析之一KafkaProducer
Kafka生产者源码解析之二RecordAccumulator
Kafka生产者源码解析之三NIO
Kafka生产者源码解析之四Sender
Kafka生产者源码解析之五小结
回忆
上几篇我们从主线程出发介绍了kafka的消息发送的过程,NIO核心组件的初始化。本篇主要从另一个辅助线程出发,详细介绍此线程的作用。
线程start
此线程是在 KafkaProducer 的构造器里初始化,并开启的
// 创建一个Senfer线程
this.sender = newSender(logContext, kafkaClient, this.metadata);
// 给此线程一个名分
String ioThreadName = NETWORK_THREAD_PREFIX + " | " + clientId;
// 将此线程嵌入到另一个线程中
this.ioThread = new KafkaThread(ioThreadName, this.sender, true);
// 开启线程,因为 KafkaThread 没有自己实现的run方法,所以走sender的run方法
this.ioThread.start();
...
public KafkaThread(final String name, Runnable runnable, boolean daemon) {
// 调用父类的构造器,传入的是sender线程
super(runnable, name);
configureThread(name, daemon);
}
run方法
启动线程,就是运行run方法,那就让我们来揭开run方法的神秘面纱。
/**
* The main run loop for the sender thread
*/
public void run() {
// 通过这个日志我们知道 这是kafka 生产者操作I/O流的线程
log.debug("Starting Kafka producer I/O thread.");
// main loop, runs until close is called
// 一直循环,直到此线程被关闭,通过running标志控制
while (running) {
try {
// 此为核心run方法,2.3详细介绍
run(time.milliseconds());
} catch (Exception e) {
log.error("Uncaught error in kafka producer I/O thread: ", e);
}
}
// 日志显示 开始关闭Kafka Producer I/O线程,发送剩余记录
log.debug("Beginning shutdown of Kafka producer I/O thread, sending remaining records.");
// okay we stopped accepting requests but there may still be
// requests in the accumulator or waiting for acknowledgment,
// wait until these are completed.
// 判断强制关闭标识是否为false,强制关闭会忽略所有未完成的操作并强制关闭sender线程
// 2.1详细介绍this.accumulator.hasUndrained()
// 2.2详细介绍this.client.inFlightRequestCount()
while (!forceClose && (this.accumulator.hasUndrained() || this.client.inFlightRequestCount() > 0)) {
try {
run(time.milliseconds());
} catch (Exception e) {
log.error("Uncaught error in kafka producer I/O thread: ", e);
}
}
// 如果强制关闭
if (forceClose) {
// We need to fail all the incomplete batches and wake up the threads waiting on
// the futures.
log.debug("Aborting incomplete batches due to forced shutdown");
this.accumulator.abortIncompleteBatches();
}
try {
// 2.4详细介绍
this.client.close();
} catch (Exception e) {
log.error("Failed to close network client", e);
}
log.debug("Shutdown of Kafka producer I/O thread has completed.");
}
2.1 RecordAccumulator 的 hasUndrained 方法
由kafka源码解析第二篇我们知道,accumulator 就是 RecordAccumulator 类,看Sender的成员变量也可得知。
/* the record accumulator that batches records */
private final RecordAccumulator accumulator;
接下来我们看下hasUndrained方法。
/**
* Check whether there are any batches which haven't been drained
*/
// 官方解释: 检查是否还有没被抽干的批处理对象
public boolean hasUndrained() {
// 遍历消息累加器的batches变量,一个读写线程安全的map集合
for (Map.Entry<TopicPartition, Deque<ProducerBatch>> entry : this.batches.entrySet()) {
// 获取集合中的双端队列
Deque<ProducerBatch> deque = entry.getValue();
synchronized (deque) {
// 如果队列不为空则返回true
if (!deque.isEmpty())
return true;
}
}
return false;
}
此方法主要是判断消息累加器中是否还有消息。
2.2 NetworkClient 的 inFlightRequestCount 方法
上一篇介绍NIO初始化的时候我们知道,Sender对象初始化的时候,是将 NetworkClient 对象传进去的。所以我们进入到此类中看下inFlightRequestCount方法
/**
* Get the number of in-flight requests
*/
@Override
// 获取已发送或正在发送但尚未收到响应的请求数
public int inFlightRequestCount() {
return this.inFlightRequests.count();
}
此方法主要判断已发送或正在发送但尚未收到响应的请求数
2.3 核心run方法
如果不是强制关闭,并且(消息累加器中还有消息或者已发送或正在发送但尚未收到响应的请求数大于0),那么我们执行核心run方法。如下:
/**
* Run a single iteration of sending
*
* @param now The current POSIX time in milliseconds
*/
void run(long now) {
// 判断维护事务对象是否为空
if (transactionManager != null) {
try {
if (transactionManager.shouldResetProducerStateAfterResolvingSequences())
// Check if the previous run expired batches which requires a reset of the producer state.
// 检查上一个运行的批次是否过期,该批次需要重置生产者状态。
transactionManager.resetProducerId();
// 判断是否有事务id
if (!transactionManager.isTransactional()) {
// this is an idempotent producer, so make sure we have a producer id
maybeWaitForProducerId();
} else if (transactionManager.hasUnresolvedSequences() && !transactionManager.hasFatalError()) {
transactionManager.transitionToFatalError(
new KafkaException("The client hasn't received acknowledgment for " +
"some previously sent messages and can no longer retry them. It isn't safe to continue."));
// 判断是否有未完成的请求