kafka-console-consumer 参数 --zookeeper 不可用 与 The coordinator is not available的问题

1,160 阅读5分钟

事情起因是这样的。我们环境需要用到kafka,现在都是使用容器化的部署,大佬就找了个github星最多docker-kafka连接给我,建议我用这个来搭建。我们开发环境已经有了zk,所以就让我用kafka连上面的zk。kafka搭起来了,生产者可以生产。但是消费者始终是消费不到消息。使用tcpdump抓包也可以确认,消息已经发送到kafka。但是消费者始终无法消费。猜想是不是那个步骤执行错了。所以就按照github.com/wurstmeiste…的官方文档一步一步来做。

wurstmeister docker-kafka tutorial:wurstmeister.github.io/kafka-docke…

发现里面有一步骤怎么也走不通:

$KAFKA_HOME/bin/kafka-console-consumer.sh --topic=topic --zookeeper=$ZK

本地查看脚本的帮助,也没有这个参数,类似功能的是--bootstrap-server的参数:


D:\Programs\kafka_2.11-2.4.0\bin\windows>kafka-console-consumer.bat --help

This tool helps to read data from Kafka topics and outputs it to standard output

.

Option                                  Description

------                                  -----------

--bootstrap-server <String: server to    REQUIRED: The server(s) to connect to.

  connect to>

--consumer-property <String:            A mechanism to pass user-defined

  consumer_prop>                          properties in the form key=value to

                                          the consumer.

--consumer.config <String: config file>  Consumer config properties file. Note

                                          that [consumer-property] takes

                                          precedence over this config.

--enable-systest-events                  Log lifecycle events of the consumer

                                          in addition to logging consumed

                                          messages. (This is specific for

                                          system tests.)

--formatter <String: class>              The name of a class to use for

                                          formatting kafka messages for

                                          display. (default: kafka.tools.

                                          DefaultMessageFormatter)

--from-beginning                        If the consumer does not already have

                                          an established offset to consume

                                          from, start with the earliest

                                          message present in the log rather

                                          than the latest message.

--group <String: consumer group id>      The consumer group id of the consumer.

--help                                  Print usage information.

--isolation-level <String>              Set to read_committed in order to

                                          filter out transactional messages

                                          which are not committed. Set to

                                          read_uncommittedto read all

                                          messages. (default: read_uncommitted)

--key-deserializer <String:

  deserializer for key>

--max-messages <Integer: num_messages>  The maximum number of messages to

                                          consume before exiting. If not set,

                                          consumption is continual.

--offset <String: consume offset>        The offset id to consume from (a non-

                                          negative number), or 'earliest'

                                          which means from beginning, or

                                          'latest' which means from end

                                          (default: latest)

--partition <Integer: partition>        The partition to consume from.

                                          Consumption starts from the end of

                                          the partition unless '--offset' is

                                          specified.



--property <String: prop>                The properties to initialize the

                                          message formatter. Default

                                          properties include:

        print.

                                          timestamp=true|false

        print.

                                          key=true|false

        print.

                                          value=true|false

        key.separator=<key.

                                          separator>

        line.separator=<line.

                                          separator>

        key.deserializer=<key.

                                          deserializer>

        value.

                                          deserializer=<value.deserializer>

                                          Users can also pass in customized

                                          properties for their formatter; more

                                          specifically, users can pass in

                                          properties keyed with 'key.

                                          deserializer.' and 'value.

                                          deserializer.' prefixes to configure

                                          their deserializers.

--skip-message-on-error                  If there is an error when processing a

                                          message, skip it instead of halt.

--timeout-ms <Integer: timeout_ms>      If specified, exit if no message is

                                          available for consumption for the

                                          specified interval.

--topic <String: topic>                  The topic id to consume on.

--value-deserializer <String:

  deserializer for values>

--version                                Display Kafka version.

--whitelist <String: whitelist>          Regular expression specifying

                                          whitelist of topics to include for

                                          consumption.

查了官方文档,也没有这个参数,类似功能的是一个--bootstrap-server的参数,在api config 里面是bootstrap.servers

kafka doc

于是很确认地,这个文档应该是写错了,于是给项目提了一个issue,建议他们修正tutorial的错误。

github.com/wurstmeiste…

然后收到一个开发者的回复说:

cricket007: --zookeeper actually is an option. Both will work equally until Zookeper is removed from Kafka

cricket007: That doesn't mean it's removed yet.

还附上了添加这个使用--bootstrap-server替代--zookeeper的官方wiki:

KIP-500: Replace ZooKeeper with a Self-Managed Metadata Quorum

大概的意思就是说,过去kafka的partitions(分区)和brokers(经纪人)的元数据存储在zk里面,而且是使用zk来选举那个broker(经纪人)做controller(控制者)。为了解决consumer对zk依赖产生其他的限制,所以就让consumer从broker获取元数据信息了。

于是我大概明白了。好的软件如果要因为更改架构设计而更改接口或者参数,会有一个弃用的过渡期。在这个过渡期一般会兼容过去的接口和参数。在过渡期中旧接口被标记为弃用,新的用户使用会逐渐减少对旧接口的依赖。经过一个过渡期后,在某一个大版本的迭代直接移除旧的接口和功能。

Elasticsearch移除type就是一个很好的例子。

所以我猜测,最新版本consumer接口的zookeeper.connect配置已经配移除了。

于是我查询kafka各个版本的文档,果然证实了自己的想法。

在kafka 0.10.0版本的时候,consumer接口新加了bootstrap.server配置,与此同时zookeeper.connect被标记为弃用(deprecated)。

在kafka 2.1.0版本的时候,consumer接口的zookeeper.connect参数再也找不到了。自然连接的脚本也无法使用这个参数了。

综上所述,如果你使用的是kafka v0.10.0版本以前的kafka,则使用kafka-console-consumer.sh只能够通过--zookeeper参数连接kafka集群。如果你使用的是kafka v0.10.0kafka v2.1.0以前的版本,则可以使用--bootstrap-server--zookeeper两个参数连接kafka集群。如果你使用的是kafka v2.1.0及以上的版本,则只能够通过--bootstrap-server参数连接kafka集群。

与此同时,我一开始遇到的问题,貌似也有头绪。因为我搭建环境的时候zookeeper已经存在的话,可能已经有kafka把元数据信息放在了zookeeper。由于之前连上的版本是kafka v0.10.0之前的版本,所以消费者必须直连zookeeper获取kafka的元数据信息。但是我后面注册到zookeeperkafka broker是基于2.4.0版本的,必须通过broker获取元数据信息。按比较好的设计来说,集群里多个版本的代码启动的服务,应该都降级到集群中最低版本的功能来运行。但是2.4.0的kafka broker已经无法使用低版本的broker controller election(经纪人控制者选举),估计是2.1.0移除的。所以导致消费者一直无法正确与kafka broker建立连接的问题,并且报出 The coordinator is not available的问题。

最终,在服务器另外启了一个干净的zookeeper解决了这个问题。。。当然这是开发环境的解决方案。如果是生产环境,同一个集群最好还是不要混用kafka版本。