在hyperledger fabric的orderer中,目前發(fā)布的版本是使用kafka來做排序,并沒有用到所謂的sbft。kafka作為一個消息中間件,來對orderer發(fā)過來的消息進行排序,這樣所有的orderer可以當做consumer來去kafka上去取消息。kafka具體和fabric怎么合作,我們按下不表。這篇文章主要介紹kafka的工作原理,以及怎樣和zookeeper合作。
Zookeeper的原理
1. Zookeeper是什么
ZooKeeper是一種為分布式應用所設計的高可用、高性能且一致的開源協(xié)調服務,它提供了一項基本服務:分布式鎖服務。由于ZooKeeper的開源特性,后來我們的開發(fā)者在分布式鎖的基礎上,摸索了出了其他的使用方法:配置維護、組服務、分布式消息隊列、分布式通知/協(xié)調等。
2. 為什么要用Zookeeper
分布式節(jié)點的各個節(jié)點的協(xié)調是分布式服務必須要解決的一個問題。舉個例子來說明
為什么要使用分布式節(jié)點呢?是為了解決所謂的單點故障。什么是單點故障呢?如下圖所示,一個主節(jié)點Master提供服務給其他機器,那么如果這個Master節(jié)點掛了呢?是不是Slave從節(jié)點就無法享受到該服務了呢?
一般對這種單點故障的解決辦法是雙Master或者多集群。如下圖所示,采用兩個Master,一個為主節(jié)點,一個為備份節(jié)點。一旦發(fā)現(xiàn)主節(jié)點有問題(掛了或者出錯),則立即切換到備份節(jié)點。
如下圖所示,所有的slave在收到消息后,直接切換到備用節(jié)點。
然而在有時候主節(jié)點沒有宕機,然而Slave節(jié)點或者其他Master節(jié)點認為他宕機了,這樣極其容易造成彼此之間的視圖(對整個集群的理解)不一致。如下圖所示:
當集群中的節(jié)點出現(xiàn)對集群的理解不一致時,會對強一致性的程序產生很壞的影響,有可能會造成消息的亂序。所以一致性問題的解決有很多辦法,比如PBFT,RAFT或者鎖等等。每種辦法都有各自的優(yōu)劣。
那么Zookeeper的引入,怎么能解決這個全局視圖保證一致呢?
如下圖所示,主節(jié)點A和主節(jié)點B在啟動的時候,都首先向Zookeeper節(jié)點注冊,Zookeeper會將這兩個節(jié)點寫入到自己的文件系統(tǒng)內,并分配給每個節(jié)點一個id號。與此同時,Zookeeper節(jié)點會保持與兩個Master節(jié)點的心跳,每隔幾個tick(自己定義在配置文件里)與節(jié)點溝通一次,以此驗證節(jié)點的狀態(tài)。這樣哪個節(jié)點宕機,哪個節(jié)點新加入等等都可以實時的反應在zookeeper上。Slave節(jié)點想知道連哪個主節(jié)點,根據自己的算法去選擇。比如kafka就會根據topic隨機選擇leader。所有人都可以在zookeeper上設置監(jiān)聽,一旦某個主節(jié)點有變動,大家都會收到zookeeper的推送消息。
寫到這里估計有人得問,你這不也會有單點故障的風險嗎?比如這個zookeeper掛了怎么辦?這個問題是個好問題。zookeeper可以設置集群,也就是幾個zookeeper可以同時對外服務。那么zookeeper集群如何保證一致性的?怎樣保證所有的zookeeper對外部世界的理解一致的?總不能這個zookeeper知道MasterA是正常的,那個zookeeper以為MasterA是不正常吧!
還是一致性的問題,這個zookeeper用了ZAB共識算法來保證各個zookeeper節(jié)點的一致性。
3. 怎樣使用Zookeeper
zookeeper的啟動需要配置如下的信息(本文以docker啟動),太坑了,這里竟然不能用代碼。
從上圖可以看出ZOO_MY_ID為每個zookeeper的唯一id號,ZOO_SERVERS為集群的ip及端口。當zookeeper起來后,可以直接進入zookeeper所在的bin目錄,查找zkCli.sh。通過該可執(zhí)行腳本可以找到注冊在zookeeper上的服務。zkCli.sh所在目錄如下圖所示:
執(zhí)行該腳本后,進入zkCli的控制臺,在該控制臺可以查看注冊到zookeeper的服務。本例以kafka來舉例:
可以到brokers目錄下查看注冊的kafka節(jié)點數目。
從上圖中,可以看到注冊到zookeeper的kafka有四個,id分別為0,1,2,3。其中0的注冊信息可以通過 get /brokers/ids/0來獲得。
各個字段的含義如下:
czxid.節(jié)點創(chuàng)建時的zxid.mzxid.節(jié)點最新一次更新發(fā)生時的
zxid.ctime.節(jié)點創(chuàng)建時的時間戳.
mtime.節(jié)點最新一次更新發(fā)生時的時間戳.
dataVersion.節(jié)點數據的更新次數.
cversion.其子節(jié)點的更新次數.
aclVersion.節(jié)點ACL(授權信息)的更新次數.
ephemeralOwner.如果該節(jié)點為ephemeral節(jié)點,ephemeralOwner值表示與該節(jié)點綁定的sessionid.如果該節(jié)點不是ephemeral節(jié)點,ephemeralOwner值為0.
dataLength.節(jié)點數據的字節(jié)數.
numChildren.子節(jié)點個數.
關于zxid的定義:ZooKeeper狀態(tài)的每一次改變, 都對應著一個遞增的Transaction id, 該id稱為zxid. 由于zxid的遞增性質, 如果zxid1小于zxid2, 那么zxid1肯定先于zxid2發(fā)生. 創(chuàng)建任意節(jié)點, 或者更新任意節(jié)點的數據, 或者刪除任意節(jié)點, 都會導致Zookeeper狀態(tài)發(fā)生改變, 從而導致zxid的值增加.
再舉個例子,下圖是kafka節(jié)點創(chuàng)建的mytopic,可以看到在0號分區(qū)的kafka主節(jié)點是0號,isr分別同步到0,1,2節(jié)點。而且通過dataVersion可以看到改變了11次,說明發(fā)了11條消息。
Kafka的原理
1. Kafka是什么
Kafka是一種分布式的,基于發(fā)布/訂閱的消息系統(tǒng)。為什么要用的消息系統(tǒng)呢?因為利用消息系統(tǒng)有如下的好處:
a)? ? 系統(tǒng)解耦。系統(tǒng)解耦帶來的好處就是擴展性增強。
b)? ? 事件分發(fā)。這樣做的好處就是帶來了異步通信,不用某個模塊等待另外一個模塊,而是將消息發(fā)送過去就不用管了,或者消費者只管去拉消息。
c)? ? 消息回溯。舉個簡單的例子,在玩游戲的過程中,想要做視頻回放,那么消息系統(tǒng)里做一次記錄即可。開個不是玩笑的玩笑,現(xiàn)在很多人拿kafka當數據庫來用。簡單方便。
d) ????......
2. 為什么要用到Kafka
a)????快速持久化,可以在O(1)的系統(tǒng)開銷下進行消息持久化。這個經過本人的驗證,的確太不可思議了,非???,源于kafka良好的順序寫模式。
b)????高吞吐,我自己測的tps可以達到幾十萬;
c)? ? 同時支持單播與廣播。同一個topic的數據,會廣播給不同的group;同一個group中的worker,只有一個worker能拿到這個數據
d)????支持存儲。很多人把kafka當數據庫來存儲東西。
e)? ? 支持分布式存儲。kafka可以和其他的kafka節(jié)點形成kafka集群,通過kafka的isr模式,topic的分區(qū)可以分布于不同的節(jié)點。
f)? ? ......
3. 怎樣使用Kafka
先介紹一下kafka的相關概念。
????Broker:指服務于Kafka的一個節(jié)點。
????topic是一個邏輯概念,用于保證Producer以及Consumer能夠通過該標示進行對接。
????partition是消息的真實存放者。partition會實際存儲在系統(tǒng)的某個目錄。它Topic的一個子概念,
????一個topic可具有多個partition,但Partition一定屬于一個topic。
下列是一個kafka集群,從下圖可以看出一共有四個節(jié)點,分別為Broker1, Broker2, Broker3, Broker4. 該集群一共有一個topic,該topic供有三個partition,分別為part0, part1, part2。其中part0分別存儲在Broker1, Broker2, Broker3上。而part1分別存儲在Broker2, Broker3, Broker4。part2分半存儲在Broker1, Broker2, Broker4中。part0的三個Broker中,Broker1為主節(jié)點。part1的主節(jié)點為Broker2, part2的主節(jié)點為Broker4。三個partition均勻分布。
需要的環(huán)境:
docker--為啥需要這個呢,因為可以很容易的模擬多機部署,當然如果你是土豪,可以忽略這個,安裝步驟在這里,當然你也可以參考別的文檔來安裝docker及docker-compose
1. 啟動kafka和zookeeper節(jié)點
利用下面的docker-compose.yml來啟動kafka與zookeeper,下面配置文件里啟動了三個zookeeper和4個kafka節(jié)點,三個zookeeper組成了一個zookeeper集群,管理kafka節(jié)點。
三個zookeeper節(jié)點分別為zookeeper0,zookeeper1,zookeeper2
四個kafka節(jié)點分別為kafka0,kafka1,kafka2,kafka3
我的zookeeper節(jié)點是基于hyperledger/fabric-zookeeper的docker鏡像來啟動的,可以到zookeeper鏡像地址下載。而kafka節(jié)點是基于hyperledger/fabric-kafka的docker鏡像來啟動的,可以到kafka鏡像地址下載。
拷貝下面配置文件,并保存到docker-compose.yml文件中。
在docker-compose.yml所在的目錄中,運行命令docker-compose up -d 命令來啟動容器。
創(chuàng)建完成后,可以進入zookeeper容器檢查kafka節(jié)點是否都已經注冊成功。
運行docker ps命令,列舉出所有的容器,如下:
進入其中的一個zookeeper容器,通過如下命令
運行zkCli.sh命令,來檢查kafka節(jié)點有沒有注冊到zookeeper上。
檢查其中的一個broker id 如下:
從中可以知道brokerid 0,1,2,3都已經注冊到zookeeper集群上了,而詳細brokerid 0的信息中可以得到其ip,端口等等。
具體kafka在zookeeper上的注冊消息圖如下,詳細我找到了一個網頁 ,可以參考
2. 創(chuàng)建topic
進入kafka節(jié)點,利用下述命令
運行下述命令,創(chuàng)建mytopic,該topic有一個分區(qū),部署有三個副本。
可以進入zookeeper節(jié)點,用zkCli.sh查看topic下的信息如下。
從中可以看出broker3為主節(jié)點,總共有3個副本,分別是broker0,broker1,broker3
3. 發(fā)送消息
進入kafka容器,利用該kafka自帶腳本可以發(fā)送消息,如下即向本kafka節(jié)點的mytopic發(fā)送消息
4. 消費消息
進入kafka容器,利用kafka自帶腳本,可以對消息進行消費,如下即向kafka節(jié)點消費消息
到zookeeper中,啟動zkCli.sh可以看到,消費者是在zookeeper中注冊了消費者id,這樣可以保障group單播。
本文參考:
https://www.cnblogs.com/wuxl360/p/5817471.html
http://blog.csdn.net/lizhitao/article/details/51718185
http://blog.csdn.net/lizhitao/article/details/23744675
http://blog.csdn.net/eric_sunah/article/details/46891901