Kafka的部署不只須要集羣可用,同時須要對orderer節點可連,這就是爲何有的時候,kafka集羣自己沒問題,可是orderer卻老是報錯。爲了試驗kafka剝離方案的可行性,跨阿里雲網絡和內網進行BAAS部署。nginx
部署環境以下:網絡
K8s部署在阿里雲環境上, 192.168.8.108可連外網,做爲master; 192.168.8.107不能連外網,做爲slave; Kafka集羣部署在內網, 192.168.9.21等機器上,均可以連外網。
由於orderer節點會起在slave機器上,也就是107這臺機器,它沒法直連外網。所以,經過nginx轉發來保證orderer能夠連上kafka集羣,以下圖所示。
函數
那麼advertised listeners的配置就尤其重要,畢竟這是kafka節點保存在zookeeper集羣中的brokers元信息,orderer最終是經過這些地址去訪問kafka的。測試
若是將kafka0的KAFKA_ADVERTISED_LISTENERS地址設爲192.168.9.21:9092,雖然集羣建立正常,可是orderer沒法連上內網地址,也就是沒法連上kafka。因此,選擇將kafka0的KAFKA_ADVERTISED_LISTENERS地址設爲192.168.8.108:9092,而後在108上設置nginx代理,轉發到 內網代理IP:9092(反向代理,經過該ip,外網能夠鏈接內網。),這樣就能夠連上kafka0節點了。阿里雲
嘗試Setupbaas 發現orderer仍然報錯kafka集羣異常,可是kafka的啓動日誌沒有任何異常。3d
setup的流程很長,還要清理環境,用kafkaclient來調試會方便不少。 fabric用的go語言client是sarama,簡單改一下fabric_test裏面的producer就能夠起一個簡單的client,來測試kafka集羣是否可用了。代理
用producer向kafka寫入數據,發現報錯信息以下:調試
報錯說明,如今這個partiton沒有leader,咱們知道kafka每一個partiton都會有一個leader,負責client的讀寫。日誌
爲了確認測試用的partition到底有沒有leader,經過kafka內部的kafka-topic.sh來查看詳細信息,結果以下圖所示:code
結果發現,topic首先是建立成功了,partition leader也是存在的,那麼爲何client沒有獲取到該partition的leader信息呢?
帶着疑問,查看sarama的部分源碼,發現傳給kafkaclient(例如orderer裏面的producer)的addrlist只是做爲seedbrokers,從seedbrokers裏面嘗試去鏈接kafka server來獲取metadata。
這個metadata裏面包括了,註冊在zk裏面的全部brokers的信息, kafkaclient其實是與這些brokers進行交互的,因此即便seedbroker填的不全,有時候也不影響kafka集羣的使用。
流程以下圖所示:
根據報錯信息,能夠發現GetMetadata返回的信息裏面有ErrLeaderNotAvailable報錯。
由上圖可知,GetMetadata向kafkabroker發送了獲取metadata的請求,而且key是3。查看kafka源碼,能夠找到kafkaAPI如何處理key爲3的請求。
跳轉到 handleTopcMetadataRequest裏面:
跳轉到getTopicMetadata:
跳轉到createTopic:
若是topic不存在,GetMetadata在zk裏面註冊topic,然而在kafka裏面把該topic標記爲無leader狀態。實際上,每一個新建的topic都是處於LEADER_NOT_AVAILABLE的狀態的,那問題應該出如今metadata的更新上面,負責管理各個partition狀態的組件是controller,是否是controller哪裏出了問題了?難道kafka啓動日誌裏有報錯被忽略了嗎?搜索Controller相關log,發現並無報錯。
ZookeeperLeaderElector: 主要用於KafkController Leader選舉,選舉出Controller是broker1,可是後續卻沒有給出controller報錯信息。實際上,controller做爲kafka的組件,日誌另有輸出,報錯以下,確實是訪問不到broker的地址。
controller是隨機選擇一個kafka節點上啓動的,爲了同步副本狀態,controller須要鏈接上每個kafka節點,由於advertised listener地址在容器裏訪問不到,因此controller與各個broker的鏈接出現異常。進入容器查看網絡鏈接狀況,經過netstat –ae發現其中一個kafka有不正常的鏈接。
經過zkCli.sh發現,這正是controller所在的kafka,能夠坐實是controller的問題了。
問題的緣由找到了,可是爲何用kafka自帶的腳本查出來的topic狀態倒是正常的呢?
查看該腳本調用的函數,發現改腳本調用的函數查詢的數據竟然來自於zk,並非從kafka中得到。由於全部kafka鏈接zk並不存在問題,因此能夠得出一致的topic 描述,看來使用這個腳本去查看topic狀態也得慎重。
GetMetadata有報錯,kafka-topic.sh卻顯示正常,終於有了解釋。
Client在GetMetadata的時候,第一次建立了無主topic,在retry的時候,kafkaclient獲取的metadata信息是來自於kafka的MetadataCache,由於controller的緣由partitionState沒有更新,因此返回的topic信息仍然有LEADER_NOT_AVAILABLE報錯。
可是爲何正常狀況,卻沒有返回這個LEADER_NOT_AVIALABLE呢?繼續往下看:
跳轉到getPartitionMetadata:
可見查詢partitionMetadata時,是經過partitionState來判斷存活的brokers裏面是否有leader。若是有partitionState未更新,就返回LEADER_NOT_AVIALABLE的metadata,不然就能夠返回最新的metadata。
Controller是如何更新partitionState的呢?
集羣全部partition狀態是由PartitionStateMachine來管理的。
controller的日誌中也可看到:
初始化partition的時候:
進入addLeaderAndIsrRequestForBrokers:
由以上代碼可見,partitionState更新須要經過ControllerChannelManager。
ControllerChannelManager負責維護Controller Leader與集羣中其餘broker之間鏈接,是管理這個集羣的基礎。然而,ControllerChannelManager在啓動時就出問題了,連不上其餘的broker,所以全部的kafka metadata都沒能更新。所以,controller必須連上advertised listeners,包括其自身所在的broker。
問題解決方案:
若是將kafka0的KAFKA_ADVERTISED_LISTENERS設爲 內網服務映射到外網的IP:9092,阿里雲192.168.8.107上卻是能夠經過修改host文件,把 內網服務映射到外網的IP 解析成192.168.8.108。這樣,107在訪問內網IP時,會連到108並經過nginx轉發到192.168.9.21:9092。orderer須要連kafka集羣的話,須要在k8s容器裏添加host才行。
問題總結:
advertised listeners不只須要讓orderer可鏈接,還須要讓每一個可能成爲controller的kafkabroker容器可連才行。
這種表面能夠建立topic,實際集羣沒法使用的狀況,能夠考慮查看controller的日誌。
kafka自帶的kafka-topic腳本,描述的是zk裏面的信息,並不必定於kafka裏面的數據一致,須要慎重使用。