【原創】為什麼我的 Kafka 總是連接失敗呢?

提出問題

近日助友 Docker 部署 Kafka 服務,服務日誌啟動正常,但客戶端卻無法連接

往日曾踩過此坑,然方法均源於部落格,其語焉不詳,不知為何不行,亦不知為何行,印象不甚深刻,耗費大量時間

為避此坑,特地學習官方文檔相關章節,讓我尋到珠絲馬跡,請聽我娓娓道來~

如嫌篇幅較長,可跳過驗證,直奔結論

本文主要記錄為何會出現無法連接到 Broker 的原因,想必看完本文你會知道該怎麼做的 🙂

謹以此文獻給那些因為無知而浪費的時光!

大膽猜測

Kafka的服務端稱為 Broker,每個 Broker 啟動時會將自己的 Broker 配置資訊上報給 Zookeeper ,如,監聽地址與埠號等,Kafka的客戶端(生產者與消費者統稱)要連接 Broker 需要經過一層認證,不通過認證就無法連接!

小心求證

測試環境:

  • GNU/Linux Debian 10 4.19.0內核
  • Docker:19.03.6
  • Docker-compose:1.17.1
  • 鏡像:zookeeper:3.5.5
  • 鏡像:wurstmeister/kafka:2.12-2.2.1

設計實驗:

使用 docker 鏡像部署一套單節點的 Zookeeper + Kafka

要求Broker監聽自定義域名(主機名)與埠,服務啟動後展示 ZookeeperBroker 的註冊資訊

使用客戶端連接 Broker ,使用不同的 --bootstrap-server 組合方式,進行驗證

開始實驗

為了提高部署效率,這裡提供一個簡單可啟動的 docker-compose-test.yml

version: "3.3"  services:      zookeeper:          image: zookeeper:3.5.5          restart: always          container_name: zookeeper          ports:              - "2181:2181"          expose:              - "2181"          environment:              - ZOO_MY_ID=1      kafka:          image: wurstmeister/kafka:2.12-2.2.1          restart: always          container_name: kafka          environment:              - KAFKA_BROKER_ID=1              - KAFKA_LISTENERS=PLAINTEXT://kafka:9090              - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181              - KAFKA_MESSAGE_MAX_BYTES=2000000          ports:              - "9090:9090"          depends_on:              - zookeeper

docker-compose-test.yml 放入你的目錄,運行部署腳本

查看兩者的日誌,檢查是否正常啟動

docker logs -f zookeeper 

docker logs -f kafka

可以看到 Kafka 已經註冊成功

讓我們查看一下,Zookeeper 中註冊的 Broker 資訊

docker exec -it zookeeper bash bin/zkCli.sh

輸入 quit 退出

想到我們已經將Kafka監聽的埠號已經映射到宿主機了,使用宿主機 IP 訪問不就得了?

先創建個Topic

docker run -it --rm --network host wurstmeister/kafka:2.12-2.2.1                bash /opt/kafka/bin/kafka-topics.sh                --bootstrap-server 192.168.1.19:9090                --create --topic logsTopic --partitions 1 --replication-factor 1

噢!又是這該死的錯誤!

想到剛才在 zookeeper 中看到的資訊,要不我把 IP 換成 kafka 不就結了?

docker run -it --rm --network host wurstmeister/kafka:2.12-2.2.1                bash /opt/kafka/bin/kafka-topics.sh                --bootstrap-server kafka:9090                --create --topic logsTopic --partitions 1 --replication-factor 1

不對,宿主機本身沒有對 kafka 作映射,問題一定出在這裡!

再次執行剛才的命令,沒有輸出,說明創建 Topic 成功!

生產一條消息測試下

docker run -it --rm --network host wurstmeister/kafka:2.12-2.2.1           bash /opt/kafka/bin/kafka-console-producer.sh           --broker-list kafka:9090 --topic logsTopic

輸入回車,發送消息; ctrl + c 斷開連接

創建消費者,消費消息測試

docker run -it --rm --network host wurstmeister/kafka:2.12-2.2.1           bash /opt/kafka/bin/kafka-console-consumer.sh           --bootstrap-server kafka:9090  --topic logsTopic  --from-beginning

嗯,消費也成功了

歸納總結

這個實驗說明了什麼呢?

說明 Kafka 客戶端在連接 Broker 的時候,Broker 將客戶端發來的請求帶的資訊與 Broker 啟動時上報給 Zookeeper 的資訊 進行了比對,比對相同則認證通過,反之建立連接失敗!

官方文檔

通過耐心地查看 Kafka 的官方文檔,終於在角落裡看到她的影子!(好久不見妹子,看文檔都眉清目秀的~)

比如下面這段:

From Kafka version 2.0.0 onwards, host name verification of servers is enabled by default for client connections as well as inter-broker connections to prevent man-in-the-middle attacks.

大意是講通過這種認證hostname的機制,來避免中間人攻擊

還有這段:

advertised.host.name: DEPRECATED: only used when advertised.listeners or listeners are not set. Use advertised.listeners instead. Hostname to publish to ZooKeeper for clients to use. In IaaS environments, this may need to be different from the interface to which the broker binds. If this is not set, it will use the value for host.name if configured. Otherwise it will use the value returned from java.net.InetAddress.getCanonicalHostName().

雖然這個參數已經標記為廢棄了,但是她提供了個資訊:如果設置主機名可能會被上報

最後看看這段

advertised.listeners: Listeners to publish to ZooKeeper for clients to use, if different than the listeners config property. In IaaS environments, this may need to be different from the interface to which the broker binds. If this is not set, the value for listeners will be used. Unlike listeners it is not valid to advertise the 0.0.0.0 meta-address.

也就是說,無論設置 listeners 還是 advertised.listeners 它們其一的資訊會被上報,供客戶端使用;只有在需要綁定不同介面時,才需要設置 advertised.listeners

結論

Kafka 客戶端在連接 Broker 的時候,Broker 將客戶端發來的請求附加資訊與 Broker 啟動時上報給 Zookeeper 的 listeners參數資訊、host(來自listeners的中間域名或主機名部分)、port (來自listeners的埠部分) 進行了驗證,認證通過建立連接執行請求,反之建立連接失敗

寫這篇文章費了好大的功夫,猜是一回事,弄明白又是一回事,如果本文對你有所幫助,歡迎點推薦與關注

引用:
https://kafka.apache.org/documentation.html

本文採用 CC BY 4.0 協議進行授權,轉載請標註作者署名及來源。
https://www.cnblogs.com/hellxz/p/why_cnnect_to_kafka_always_failure.html