聊聊 Kafka: Producer 的网络模型

简介: 聊聊 Kafka: Producer 的网络模型

一、Producer 的网络模型

我们前面几篇有说 Producer 发送流程的源码分析,但那个是大的轮廓,涉及到发送很多相关的内容,比如:

  • 获取 topic 的 metadata 信息
  • key 和 value 的序列化
  • 获取该 record 要发送到的 partition
  • 向 RecordAccmulator 中追加 record 数据
  • 唤醒 sender 线程发送 RecordBatch



那这篇老周主要来说下 Producer 的网络模型,这里直接给出 Producer 的网络模型图,如下:


从图中可以看出,KafkaProducer 相当于客户端,与 Sender 调用层交互,Sender 调用 NetworkClient,NetworkClient 调用 Selector,而 Selector 底层封装了 Java NIO 的相关接口。心中有了 Producer 的网络模型大致轮廓后,我们接下来就可以来分析 Producer 的网络模型。

二、Producer 与 Broker 的交互流程

我们在业务代码通过生产者 producer 调用 send 方法来发送消息,不难发现都是通过走 Producer 的实现类 KafkaProducer 的 send 方法:

2.1 org.apache.kafka.clients.producer.KafkaProducer#doSend

上面的两个 send 方法最终会走到 doSend 方法里来:

这块的源码老周在前两篇的 Producer 源码解析那一篇分析了的哈,这里主要说下与 Broker 通信的交互分析。主要有两点:

  • waitOnMetadata():请求 tp(topic-partition)元数据 metadata 更新,中间会调用 sender.wakeup()。
  • accumulator.append():将 record 对应的 tp 写入到 deque 中,如果该 tp 对应的 deque batch 是满了或者新建了一个 batch,则会调用 sender.wakeup()。


主要看下 sender.wakeup() 方法,主要作用就是将 Sender 线程从阻塞中唤醒。

2.2 org.apache.kafka.clients.producer.internals.Sender#wakeup

/**
 * Wake up the selector associated with this send thread
 */
public void wakeup() {
    this.client.wakeup();
}
/**
 * Interrupt the client if it is blocked waiting on I/O.
 */
@Override
public void wakeup() {
    this.selector.wakeup();
}
/**
 * Interrupt the nioSelector if it is blocked waiting to do I/O.
 */
@Override
public void wakeup() {
    this.nioSelector.wakeup();
}

不难发现,调用链是:

Sender -> NetworkClient -> Selector(Kafka 封装的)-> Selector(java.nio.channels.Selector Java NIO)

wakeup() 的主要作用就是唤醒阻塞在 select()/select(long) 上的线程,为什么要唤醒?因为注册了新的 channel 或者事件。

再回到 Kafka 这里,KafkaProducer 中 dosend() 方法调用 sender.wakeup() 方法作用就很明显了。作用就是:当有新的 RecordBatch 创建后,旧的 RecordBatch 就可以发送了,如果线程阻塞在 select() 方法中,就将其唤醒,Sender 重新开始运行 run() 方法,在这个方法中,旧的 RecordBatch 将会被选中,进而可以及时将这些请求发送出去。

2.3 org.apache.kafka.clients.producer.internals.Sender#run

跟到 runOnce 方法里去:

继续跟,核心是 Sender 线程每次循环具体执行的地方,即 sendProducerData() 方法:

最后调用 client.poll() 方法,关于 socket 的一些实际的读写操作。

我们来小结一下 Sender.run() 方法的大致流程,主要分为以下五步:

  • accumulator.ready():遍历整个 batches(key:TopicPartition,value: Deque>),如果 ProducerBatch 不为空,就将其对应的 leader 选出来,最后会返回一个可以发送 ReadyCheckResult 实例,readyNodes 是主要的成员变量。


  • 如果有 tp 的 leader 是未知的,就强制 metadata 更新。遍历未知 leader 的主题(包含 leader 选举未决的主题以及可能已经过期的主题),再次将主题添加到元数据以确保它被包含并请求元数据更新,因为有消息要发送到该主题,调用 requestUpdate() 方法来更新。
  • accumulator.drain():遍历每个 leader (第一步中选出)节点,获取该节点上所有的 tp,如果该 tp 对应的 ProducerBatch 不在 backoff 期间(没有重试过或者重试了但是间隔已经达到了 retryBackoffMs),并且 ProducerBatch 的大小不大于 maxSize(一个 request 的最大限制默认 1 MB)或 ProducerBatch 的集合是空的,那么就把这个 ProducerBatch 添加 list 中,最终返回的类型为 Map<Integer, List>,key 为 leader.id,value 为要发送的 ProducerBatch 的列表。
  • sendProduceRequests():发送 Producer 请求,这个方法会调用 NetworkClient.send() 来发送 clientRequest。
  • NetworkClient.poll():关于 socket 的一些实际的读写操作,这个方法会继续调用 Kafka 封装的 Selector.poll(),跟进去底层是调用的 Java NIO 的 Selector.poll()。

2.4 org.apache.kafka.clients.NetworkClient#poll

主要分为以下三步:

  • metadataUpdater.maybeUpdate():判断是否需要更新 metadata 元数据。选择具有最少未完成请求且至少符合连接条件的节点。此方法将更喜欢具有现有连接的节点,但如果所有现有连接都在使用,则可能会选择我们还没有连接的节点。如果不存在连接,则此方法将首选最近连接尝试最少的节点。这种方法永远不会选择一个没有现有连接的节点,并且我们在重新连接回退期间断开了连接,或者选择了一个被限制的活动连接。
  • selector.poll():进行 socket 相关的 IO 操作。
  • 处理完成后的操作:在一个 select() 过程之后的相关处理
  • handleAbortedSends(responses):如果由于不受支持的版本异常或断开连接而中止发送,请立即处理,无需等待 Selector#poll。
  • handleCompletedSends(responses, updatedNow):处理任何已完成的请求发送。特别是如果预期没有响应,则认为请求已完成。
  • handleCompletedReceives(responses, updatedNow):处理任何已完成的接收并使用接收到的响应更新响应列表。(MetadataResponse、ApiVersionsResponse 都是在这处理的)
  • handleDisconnections(responses, updatedNow):处理连接失败的那些连接,重新请求 metadata。
  • handleConnections():处理新建立的那些连接(还不能发送请求,比如:还未认证)
  • handleInitiateApiVersionRequests(updatedNow):对那些新建立的连接,发送 apiVersionRequest(默认情况:第一次建立连接时,需要向 Broker 发送 ApiVersionRequest 请求)
  • handleTimedOutRequests(responses, updatedNow):处理 timeout 的连接,关闭该连接,并刷新 metadata。

2.5 org.apache.kafka.common.network.Selector#poll

Kafka 中的 Selector 类主要是 Java NIO 相关接口的封装,Socket 相关 IO 操作都是在这个类中完成的。主要操作都是在下面这个方法中调用的:

2.5.1 org.apache.kafka.common.network.Selector#clear

2.5.2 org.apache.kafka.common.network.Selector#select

Selector.select() 方法底层还是调用的 Java NIO 的原生接口,这里的 nioSelector 其实就是 java.nio.channels.Selector 的实例对象,这个方法最坏情况下,会阻塞 ms 的时间,如果在一次轮询,只要有一个 Channel 的事件就绪,它就会立马返回。

/**
 * Check for data, waiting up to the given timeout.
 *
 * @param timeoutMs Length of time to wait, in milliseconds, which must be non-negative
 * @return The number of keys ready
 */
private int select(long timeoutMs) throws IOException {
    if (timeoutMs < 0L)
        throw new IllegalArgumentException("timeout should be >= 0");
    if (timeoutMs == 0L)
        return this.nioSelector.selectNow();
    else
        return this.nioSelector.select(timeoutMs);
}

2.5.3 org.apache.kafka.common.network.Selector#pollSelectionKeys

这部分代码是 socket IO 的主要部分,发送 Send 及接收 Receive 都是在这里完成的,在 poll() 方法中,这个方法会调用三次:

  • 处理从具有缓冲数据的通道的轮询事件
  • 处理已经就绪的事件,进行相应的 IO 操作。
  • 处理新建立的那些连接,添加缓存及传输层的握手与认证。

2.5.4 org.apache.kafka.common.network.Selector#addToCompletedReceives

/**
 * adds a receive to completed receives
 */
private void addToCompletedReceives(KafkaChannel channel, NetworkReceive networkReceive, long currentTimeMs) {
    if (hasCompletedReceive(channel))
        throw new IllegalStateException("Attempting to add second completed receive to channel " +                      channel.id());
    // 添加到 completedReceives 中
    this.completedReceives.put(channel.id(), networkReceive);
    sensors.recordCompletedReceive(channel.id(), networkReceive.size(), currentTimeMs);
}

接收到的所有 Receive 都会被放入到 completedReceives 的集合中等待后续处理。

三、总结

总结的话我就不复述了,上面的源码流程说的很清楚了。最后再来一张 Producer 网络模型的时序图:

希望对你有所帮助,我们下期再见。



Java

相关文章
|
3天前
|
网络协议 安全 网络安全
探索网络模型与协议:从OSI到HTTPs的原理解析
OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
26 1
|
8天前
|
监控 安全 BI
什么是零信任模型?如何实施以保证网络安全?
随着数字化转型,网络边界不断变化,组织需采用新的安全方法。零信任基于“永不信任,永远验证”原则,强调无论内外部,任何用户、设备或网络都不可信任。该模型包括微分段、多因素身份验证、单点登录、最小特权原则、持续监控和审核用户活动、监控设备等核心准则,以实现强大的网络安全态势。
|
2月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于BP神经网络的苦瓜生长含水量预测模型matlab仿真
本项目展示了基于BP神经网络的苦瓜生长含水量预测模型,通过温度(T)、风速(v)、模型厚度(h)等输入特征,预测苦瓜的含水量。采用Matlab2022a开发,核心代码附带中文注释及操作视频。模型利用BP神经网络的非线性映射能力,对试验数据进行训练,实现对未知样本含水量变化规律的预测,为干燥过程的理论研究提供支持。
|
1月前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
87 2
|
1月前
|
运维 网络协议 算法
7 层 OSI 参考模型:详解网络通信的层次结构
7 层 OSI 参考模型:详解网络通信的层次结构
170 1
|
2月前
|
网络协议 前端开发 Java
网络协议与IO模型
网络协议与IO模型
138 4
网络协议与IO模型
|
2月前
|
机器学习/深度学习 网络架构 计算机视觉
目标检测笔记(一):不同模型的网络架构介绍和代码
这篇文章介绍了ShuffleNetV2网络架构及其代码实现,包括模型结构、代码细节和不同版本的模型。ShuffleNetV2是一个高效的卷积神经网络,适用于深度学习中的目标检测任务。
109 1
目标检测笔记(一):不同模型的网络架构介绍和代码
|
1月前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
1月前
|
机器学习/深度学习 人工智能 算法
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
车辆车型识别,使用Python作为主要编程语言,通过收集多种车辆车型图像数据集,然后基于TensorFlow搭建卷积网络算法模型,并对数据集进行训练,最后得到一个识别精度较高的模型文件。再基于Django搭建web网页端操作界面,实现用户上传一张车辆图片识别其类型。
87 0
【车辆车型识别】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+算法模型
|
2月前
|
开发者
什么是面向网络的IO模型?
【10月更文挑战第6天】什么是面向网络的IO模型?
24 3