用户线程
用户循环调用KafkaProducer.send(ProducerRecord<K,V>record,Callbackcallback)
->KafkaProducer.doSend
–>阻塞等待topic的元数据信息->等待sender线程更新元数据
–>accumulator.append
—>根据Topic-Partition信息从batches获取Deque,取双向队列的最后一个执行last.tryAppend(队列为空的时候要收集空间new一个RecordBatch入队)
–>结束
Sender线程
KafkaProducer构造的时候启动一个Sender线程循环调用
–>run
—>accumulator.ready
---->batches迭代调用,查找里面每个<TopicPartition,Deque>,获取TopicPartition的leader节点,从Deque取第一个RecordBatch,判断这个RecordBatch已经准备好了,就把leader节点信息放到readyNodes里面,构造ReadyCheckResult(readyNodes,nextReadyCheckDelayMs,unknownLeadersExist)返回
—>readyNodes迭代调用,判断NetworkClient里面该节点是否就绪:
NetworkClient为每个节点维护转态的表connectionStates
selector为每个节点维护channel的表channels
inFlightRequests为每个节点维护Deque,放在requests里面
---->如果没有就绪则initiateConnect:
connectionStates.connecting
channels.put(id,channel)
—>如果没有就绪则从readyNodes中移除该节点
—>就绪了则执行this.accumulator.drain(cluster,result.readyNodes,this.maxRequestSize,now)
---->readyNodes的每个节点:
从集群cluster中取得节点的分区信息Listparts;
从中顺序取一个分区构造TopicPartition,根据这个TopicPartition从batches里面取队列deque,取队列的第一个RecordBatch元素,判断满足一些条件,把batch放到ready里面
再取下一个分区构造,直到总大小maxSize。//这里是取了一个节点的多个分区队列,每个队列的第一个RecordBatch元素
放到Map<Integer,List>里面,node->List
—>从accumulator里面把上面收集的batch的TopicPartition执行mutePartition操作
—>accumulator.abortExpiredBatches把batches里面的超时的RecordBatch移除
—>上面收集的batches的每个元素执行createProduceRequests//每个节点执行一次
---->把Listbatches里面的batch放到produceRecordsByPartition里面,构造ProduceRequest
----->groupDataByTopic组成topic-partition-buffer,构造RequestSend,构造callback,构造ClientRequest
—>每个ClientRequest调用client.send(request,now)
---->doSend
----->this.inFlightRequests.add(request);
----->selector.send(request.request());
------>channel.setSend(send);
—>client.poll(pollTimeout,now)
---->metadataUpdater.maybeUpdate->构造更新元数据的请求,但是为什么发到最空闲的node?随便一个node都可以更新元数据吗??
---->selector.poll
---->处理response和回调
–>结束