十分钟,了解Kafka的Sender线程

〇、前言

在上两篇文章《连Producer端的主线程模块运行原理都不清楚,就敢说自己精通Kafka》和《一文了解Kafka的消息收集器RecordAccumulate》中,我们介绍了Main ThreadRecordAccumulate 的工作原理,那么在本篇文章中,我们继续介绍第三部分内容:Sender线程

在介绍原理之前,大家再重温一下Producer端的整体架构,图示如下所示:

null

这个图看不懂没有关系,我们会在介绍Producer端原理时一一介绍每个部分的含义及其所复杂的功能。

一、Sender线程

除了我们前面曾经介绍过的Main Thread主线程之外,在KafkaProducer中还启动了一个Sender线程,那么,本节我们就来针对Sender线程进行解析,Send线程启动代码如下所示:

null

Sender线程负责从RecordAccumulate中获取缓存消息,在获取了以Map<TopicPartition,Deque> 的对应关系存储的消息缓存之后,会通过主题信息分区信息创建TopicPartition实例对象tp,然后再以此为key,获取ProducerBatch的双向队列,如下所示:

null

然后,会进一步将映射中key的类型从TopicPartition转换为NodeId,即:Map<NodeId,Deque> 的对应关系。这是由于当Producer端最终发送消息的时候,关注的是向哪个Broker节点发送消息,而并不是关心哪个主题分区,所以此处需要做一个从应用逻辑层面向网络I/O层面的转换。如下所示:

null

当最后要进行消息发送的时候,还要再次进行封装,封装出用于消息发送的ProduceRequest,此时的对应关系就变成了NodeId和ProduceRequest了,代码如下所示:

null

请求在从Sender线程发往Kafka之前还会保存到InFlightRequests 中,InFlightRequests保存对象的具体形式为 Map<NodeId,Deque<Request>>,它的主要作用是缓存了已经发出去但还没有收到响应的请求。代码如下所示:

null

如果我们开启幂等配置,则会创建TransactionManager实例对象,如下所示:

null

当TransactionManager实例对象不为null的时候,Sender现成则会执行下图红框内的代码逻辑,那么在黄框中,我们会看到调用了maybeSendAndPollTransactionalRequest()方法,代码如下所示:

null

在选择目标节点的时候,如果coordinatorType为空,则会调用client的leastLoadeNode(...)方法,通过该方法可以获得所有Node中负载最小 的那一个。那怎么来判断Node节点的负载呢? 在上面的内容中,我们其实提到了,发出去的消息也会保存到InFlightRequests中,它其实是一个缓存的作用,主要用来缓存已经发出去但是还没有接收到响应确认的消息请求。因此,我们可以通过它来判断那些Node节点未接到响应的请求数量最少,则就是负载最小的。通过这种方式,可以保证负载尽可能的平均,而不会造成某一个节点的重度阻塞从而影响整个消息的发送性能。代码如下所示:

null

除了上面之外,我们在元数据更新的时候,也是通过leastLoadeNode(...)方法来获得负载最低的节点的,那么,在Kafka中什么是元数据呢? 当我们发送消息的时候,消息发送到哪个分区,这个分区对应的Broker的地址和端口,已经这个是否配置了Kafka集群,集群中都包含哪些节点等等,都是保存在元数据信息中的。那么,在什么步骤除非了元数据更新呢? 我们可以把视野转向Sender的runOnce()方法上,在下图红框处,我们调用了client的poll方法,如下是该方法的源码:

null

此处的client对应的是NetworkClient的实例对象,在该类的poll(...)方法中,执行了更新元数据的逻辑,即下图红框所示:

null

maybeUpdate方法中,我们看到了熟悉的一段代码Node node = leastLoadedNode(now); 此处就是获得负载最低节点的地方。那么获得到了这个node之后,就可以调用maybeUpdate(now, node)来尝试更新元数据信息了:

null

maybeUpdate(now, node)方法中我们可以看到,更新元数据也是采用发送消息的方式,即:向这个负载最低的Node发送MetadataRequest请求来获取具体的元数据信息。在创建完MetadataRequest之后同样会存入InFlightRequests,之后的步骤就和发送消息时的处理逻辑一样的:

null

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享

更多技术干货,欢迎大家关注公众号"爪哇缪斯" ~ (^o^)/ ~ 「干货分享,每天更新」

相关推荐
源码121510 分钟前
ASP.NET MVC宠物商城系统
后端·asp.net·宠物
nameofworld34 分钟前
前端面试笔试(二)
前端·javascript·面试·学习方法·数组去重
程序员劝退师_1 小时前
Kafka学习笔记
笔记·学习·kafka
scc21401 小时前
kafka:使用flume自定义拦截器,将json文件抽取到kafka的消息队列(topic)中,再从topic中将数据抽取到hdfs上
hdfs·kafka·flume
摇光931 小时前
promise
前端·面试·promise
scc21401 小时前
kafka中topic的数据抽取不到hdfs上问题解决
分布式·hdfs·kafka
Ai 编码助手1 小时前
Go语言 实现将中文转化为拼音
开发语言·后端·golang
hummhumm1 小时前
第 12 章 - Go语言 方法
java·开发语言·javascript·后端·python·sql·golang
杜杜的man1 小时前
【go从零单排】Directories、Temporary Files and Directories目录和临时目录、临时文件
开发语言·后端·golang
wywcool2 小时前
JVM学习之路(5)垃圾回收
java·jvm·后端·学习