你真的了解bootstrap.servers这个参数么?_java

bootstrap.servers这个参数是常用的KafkaProducer和KafkaConsumer用来连接Kafka集群的入口参数,这个参数对应的值通常是Kafka集群中部分broker的地址,比如:host1:9092,host2:9092,不同的broker地址之间用逗号隔开。这个参数使用的比较频繁,久而久之的就会认为这个参数配置的是所要连接的Kafka集群的broker地址,包括很多Kafka的初学者而言也会Keep这个观点,其实这个是不准确的。bootstrap.servers这个参数是用来配置发现Kafka集群信息的,这个意味着什么呢?

KafkaProducer与Kafka集群建立连接的过程是:

  1. KafkaProducer向bootstrap.servers所配置的地址指向的Server发送MetadataRequest请求;

  2. bootstrap.servers所配置的地址指向的Server返回MetadataResponse给KafkaProducer,MetadataResponse中包含了Kafka集群的所有元数据信息;

  3. KafkaProducer与Kafka集群建立TCP连接。

在绝大多数的时候,我们的Kafka集群扮演着第1和第2步骤中的Server角色,如下图中的左边。然而完全可以将这个Server的角色剥离出Kafka集群,如下图中的右边,我们可以自定义一个Server,这个Server的功能是接收MetadataRequest请求,并返回MetadataResponse。

你真的了解bootstrap.servers这个参数么?_java_02

我们可以在这个Server所返回的MetadataResponse中大做文章,可以添加路由的功能、LoadBalance的功能等。如下图所示,我们还可以将一个Kafka集群专门用作Server,比如下面的Kafka Cluster1(实现这个功能要修改Kafka core的代码),让其返回其他集群的元数据信息,包装进MetadataResponse中。笔者并不建议使用这里提及的骚操作,首先是没有什么必要,玩不溜也很容易玩脱。这里提及这些概念只是让大家更加深刻的了解bootstrap.servers参数其本质的含义。

你真的了解bootstrap.servers这个参数么?_java_03

在旧版的生产者(Scala版本)中还没有bootstrap.servers这个参数,与此对应的是metadata.broker.list参数。metadata.broker.list这个参数很直观,metadata表示元数据,broker.list表示broker的地址列表,从取名中我们可以看出这个参数很直接的表示所要连接的kafka broker的地址,以此获取元数据。而新版生产者的bootstrap.servers参数这个取名就比较有内涵了,可以直观的翻译为“引导程序的服务地址”,这个在取名上就多了一层“Proxy”的空间,让人可以遐想出Server与Cluster的分离的可能。

在旧版的消费者(Scala版本)中也没有bootstrap.servers这个参数,与此对应的是zookeeper.connect参数。很多读者都是从0.8.x版本开始用到现在的2.0.0版本,随着版本的变迁,对于客户端中会出现bootstrap.servers、metadata.broker.list、zookeeper.connect往往也是很迷糊。这一点还存在Kafka所提供的诸多脚本之中,在这些脚本中连接Kafka采用的选项参数有--bootstrap-server,有--broker-list,还有--zookeeper(分别与前面的3个参数对应)这个让很多Kafka的老司机也难辨别到底哪个脚本该用哪个参数。

随着版本的更替--broker-list(metadata.broker.list)渐渐的退出历史的舞台,但是有意思的是截止目前最新的2.0.0版本中还并没有完全的抛弃对--broker-list的使用,下面截取的是官网上的示例,可以看到在使用kafka-console-producer.sh脚本时还是在使用--broker-list。

你真的了解bootstrap.servers这个参数么?_java_04

Kafka2.0.0已经删除了旧版生产者的代码,照理说应该不再需要--broker-list了,完全可以使用--bootstrap-server。如果你在命令行中直接键入kafka-console-producer.sh而不带任何参数,那么就会像下图中的那样提示出所有可用的参数信息。

你真的了解bootstrap.servers这个参数么?_java_05

然而在你仔细遍寻一番的时候,你会发现在kafka-console-producer.sh脚本中就没有bootstrap-server的选项,对此Kafka意欲何为呢?为了逼近真相,我们深入到kafka-console-producer.sh脚本内部来一探究竟,其代码实现是kafka.tools.ConsoleProducer,而ConsoleProducer内部封装的是KafkaProducer,照理说KafkaProducer接收bootstrap.servers这样的参数而不接收broker.list这样的参数。
在初始化KafkaProducer时有这样一句代码:

props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIGconfig.brokerList)

显然,内部的KafkaProducer用的是bootstrap.servers,那么这里的config.brokerList又是什么呢?我们再来看一下下面的两行代码:

val brokerList = options.valueOf(brokerListOpt) 
val brokerListOpt = parser.accepts("broker-list""REQUIRED: 
The broker list string in the form HOST1:PORT1,HOST2:PORT2."
)
  .withRequiredArg
  .describedAs("broker-list")
  .ofType(classOf[String])

第一行代码中的brokerList就是前面KafkaProducer中bootstrap.servers参数对应的值config.brokerList,而这个brokerList的值就是--broker-list参数所指向的值,如此我们就明白了kafka-console-producer.sh脚本对外只是使用了--broker-list参数,而对内又转化为了bootstrap.servers,如此是否显得多次一举?

笔者翻看了另外3个版本(0.8.2.2、1.0.0和1.1.0)的Kafka代码,这些版本的ConsoleProducer中的内部包装的有两种类型的生产者,分别为OldProducer和NewShinyProducer。OldProducer内部又是包装的年代久远的旧版的生产者kafka.producer.Producer,注意它使用的是metadata.broker.list。NewShinyProducer内部包装的是KafkaProducer,它使用的才是bootstrap.servers。最开始的时候只有OldProducer使用的是--broker-list,对应于“metadata.broker.list”,随着版本的推进又加入了NewShinyProducer,为了保持兼容也延续了--broker-list。

既然最新的最新的2.0.0版本的Kafka中已经删除了kafka.producer.Producer的代码,也就是说完全摒弃了OldProducer。那么为什么还沿用--broker-list呢?只能说为了兼容吧,笔者更贴近于的说法是:忘了改了或者是懒得改了,认为这样没有必要。这里可以像kafka-console-consumer.sh一样使用--bootstrap-server,或者在kafka-console-producer.sh脚本中添加一个--bootstrap-server的参数,然后标注--broker-list为Deprecated,以期在后续的版本中能够切换过来。

值得庆幸的是kafka-console-consumer.sh完全摒弃了之前的--zookeeper,但是这并不代表着--zookeeper在其他脚本中也无用武之地。很常用的kafka-topics.sh脚本就保留了--zookeeper,因为kafka-topics.sh操作的是zookeeper中的节点,KafkaController会Watch这些节点的变更,然后在进行下一步的操作。对于每个脚本而言,如果你掌握了它们的实现原理之后再来分辨使用--zookeeper还是--bootstrap-server亦或者是--broker-list就显得非常容易了。

如果你足够了解Kafka变迁的历史,并且你也足够的了解Kafka的核心原理,那么你在面对着这个三个参数的时候绝对能够明辨是非而不被混淆;如果不行说明你功力尚浅,还需多多修炼,修炼入口就在下方:

你真的了解bootstrap.servers这个参数么?_java_06

欢迎加入群聊【消息生态圈】,与各位道友们谈谈自己的想法。