Kafka学习笔记:生产者 TCP 连接

Kafka 生产者程序概览

Kafka 的 Java 生产者 API 主要的对象就是 KafkaProducer。通常我们开发一个生产者的步骤有 4 步。

第 1 步:构造生产者对象所需的参数对象。

第 2 步:利用第 1 步的参数对象,创建 KafkaProducer 对象实例。

第 3 步:使用 KafkaProducer 的 send 方法发送消息。

第 4 步:调用 KafkaProducer 的 close 方法关闭生产者并释放各种系统资源。上面这 4 步写成 Java 代码的话大概是这个样子:

扫描二维码关注公众号,回复: 8585983 查看本文章

Properties props = new Properties ();
props.put(“参数1”, “参数1的值”);
props.put(“参数2”, “参数2的值”);
……
try (Producer<String, String> producer = new KafkaProducer<>(props)) {
            producer.send(new ProducerRecord<String, String>(……), callback);
  ……
}

 这段代码使用了 try-with-resource 特性,所以并没有显式调用 producer.close() 方法。

无论是否显式调用 close 方法,所有生产者程序大致都是这个路数。

何时创建 TCP 连接?

TCP 连接是在创建 KafkaProducer 实例时建立的。在创建 KafkaProducer 实例时,生产者应用会在后台创建并启动一个名为 Sender 的线程,该 Sender 线程开始运行时首先会创建与 Broker 的连接。它会连接 bootstrap.servers 参数指定的所有 Broker. Producer 向某一台 Broker 发送了 METADATA 请求,尝试获取集群的元数据信息

 

 

 bootstrap.servers 参数。它是 Producer 的核心参数之一,指定了这个 Producer 启动时要连接的 Broker 地址。请注意,这里的“启动时”,代表的是 Producer 启动时会发起与这些 Broker 的连接。如果你为这个参数指定了 1000 个 Broker 连接信息,那么很遗憾,你的 Producer 启动时会首先创建与这 1000 个 Broker 的 TCP 连接。通常你指定 3~4 台就足以了。
 

KafkaProducer 实例创建的线程和前面提到的 Sender 线程共享的可变数据结构只有 RecordAccumulator 类,故维护了 RecordAccumulator 类的线程安全,也就实现了 KafkaProducer 类的线程安全。它主要的数据结构是一个 ConcurrentMap<TopicPartition, Deque>。TopicPartition 是 Kafka 用来表示主题分区的 Java 对象,本身是不可变对象。而 RecordAccumulator 代码中用到 Deque 的地方都有锁的保护,所以基本上可以认定 RecordAccumulator 类是线程安全的.

TCP 连接还可能在两个地方被创建:一个是在更新元数据后,另一个是在消息发送时。

因为这两个地方并非总是创建 TCP 连接。当 Producer 更新了集群的元数据信息之后,如果发现与某些 Broker 当前没有连接,那么它就会创建一个 TCP 连接。同样地,当要发送消息时,Producer 发现尚不存在与目标 Broker 的连接,也会创建一个。

 

Producer 更新集群元数据信息的两个场景。

场景一:当 Producer 尝试给一个不存在的主题发送消息时,Broker 会告诉 Producer 说这个主题不存在。此时 Producer 会发送 METADATA 请求给 Kafka 集群,去尝试获取最新的元数据信息。

场景二:Producer 通过 metadata.max.age.ms 参数定期地去更新元数据信息。该参数的默认值是 300000,即 5 分钟,也就是说不管集群那边是否有变化,Producer 每 5 分钟都会强制刷新一次元数据以保证它是最及时的数据。

何时关闭 TCP 连接

Producer 端关闭 TCP 连接的方式有两种:一种是用户主动关闭;一种是 Kafka 自动关闭。

第一种。这里的主动关闭实际上是广义的主动关闭,甚至包括用户调用 kill -9 主动“杀掉”Producer 应用。当然最推荐的方式还是调用 producer.close() 方法来关闭。

第二种是 Kafka 帮你关闭,这与 Producer 端参数 connections.max.idle.ms 的值有关。

默认情况下该参数值是 9 分钟,即如果在 9 分钟内没有任何请求“流过”某个 TCP 连接,那么 Kafka 会主动帮你把该 TCP 连接关闭。

用户可以在 Producer 端设置 connections.max.idle.ms=-1 禁掉这种机制。

一旦被设置成 -1,TCP 连接将成为永久长连接。

当然这只是软件层面的“长连接”机制,由于 Kafka 创建的这些 Socket 连接都开启了 keepalive,因此 keepalive 探活机制还是会遵守的。

TCP 连接是在 Broker 端被关闭的,但其实这个 TCP 连接的发起方是客户端,因此在 TCP 看来,这属于被动关闭的场景,即 passive close。被动关闭的后果就是会产生大量的 CLOSE_WAIT 连接,因此 Producer 端或 Client 端没有机会显式地观测到此连接已被中断。

对Kafka(2.1.0)而言,Java Producer 端管理 TCP 连接的方式是:

1.KafkaProducer 实例创建时启动 Sender 线程,从而创建与 bootstrap.servers 中所有 Broker 的 TCP 连接。

2.KafkaProducer 实例首次更新元数据信息之后,还会再次创建与集群中所有 Broker 的 TCP 连接。

3.如果 Producer 端发送消息到某台 Broker 时发现没有与该 Broker 的 TCP 连接,那么也会立即创建连接。

4.如果设置 Producer 端 connections.max.idle.ms 参数大于 0,则步骤 1 中创建的 TCP 连接会被自动关闭;如果设置该参数 =-1,那么步骤 1 中创建的 TCP 连接将无法被关闭,从而成为“僵尸”连接。


原文引用:

Kafka核心技术与实战 - 胡夕
 

发布了295 篇原创文章 · 获赞 783 · 访问量 32万+

猜你喜欢

转载自blog.csdn.net/zhanglong_4444/article/details/103690254
今日推荐