十分钟,了解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^)/ ~ 「干货分享,每天更新」

相关推荐
yang_shengy41 分钟前
【JavaEE】Spring(1)
java·后端·spring·java-ee
m0_748254882 小时前
芋道源码(无遮羞布版)Spring Boot 全景指南
java·spring boot·后端
步、步、为营4 小时前
解锁新技能:Windows Forms与ASP.NET API的梦幻联动
windows·后端·asp.net
雨 子4 小时前
Spring Boot 日志
java·spring boot·后端·log4j
技术的探险家4 小时前
R语言的文件操作
开发语言·后端·golang
violin-wang4 小时前
SpringBoot的Bean-高级-第三方Bean以及Bean管理
java·spring boot·后端·bean
m0_748230215 小时前
从 0 开始实现一个 SpringBoot + Vue 项目
vue.js·spring boot·后端
计算机学姐5 小时前
基于SpringBoot的健身房管理系统
java·vue.js·spring boot·后端·mysql·spring·mybatis
BinaryBardC5 小时前
Go语言的文件操作
开发语言·后端·golang
bing_1585 小时前
Spring Boot 中使用 ShardingSphere-Proxy
java·spring boot·后端