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."));
                // 判断是否有未完成的请求
                
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值