分佈式架構(2):Zookeeper
1、Zookeeper
1.1什麼是zookeeper
ZooKeeper是一個分佈式的,開放源碼的分佈式應用程序協調服務,是Google的Chubby一個開源的實現,它是集羣的管理者,監視着集羣中各個節點的狀態根據節點提交的反饋進行下一步合理操作。最終,將簡單易用的接口和性能高效、功能穩定的系統提供給用戶。
參照博客:https://www.cnblogs.com/felixzh/p/5869212.html
1.2 ZooKeeper提供了什麼?
-
文件系統
-
通知機制
1.2.1 Zookeeper文件系統
每個子目錄項如 NameService 都被稱作爲znode,和文件系統一樣,我們能夠自由的增加、刪除znode,在一個znode下增加、刪除子znode,唯一的不同在於znode是可以存儲數據的。
有四種類型的znode:
1、PERSISTENT-持久化目錄節點
客戶端與zookeeper斷開連接後,該節點依舊存在
2、PERSISTENT_SEQUENTIAL-持久化順序編號目錄節點
客戶端與zookeeper斷開連接後,該節點依舊存在,只是Zookeeper給該節點名稱進行順序編號
3、EPHEMERAL-臨時目錄節點
客戶端與zookeeper斷開連接後,該節點被刪除
4、EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節點
客戶端與zookeeper斷開連接後,該節點被刪除,只是Zookeeper給該節點名稱進行順序編號
1.2.2 Zookeeper通知機制
客戶端註冊監聽它關心的目錄節點,當目錄節點發生變化(數據改變、被刪除、子目錄節點增加刪除)時,zookeeper會通知客戶端。
1.3zookeeper能做什麼?
- 命名服務
- 配置管理
- 集羣管理
- 分佈式鎖
- 隊列管理
1.3.1命名服務
在zookeeper的文件系統裏創建一個目錄,即有唯一的path。在我們使用tborg無法確定上游程序的部署機器時即可與下游程序約定好path,通過path即能互相探索發現。
1.3.2配置管理
程序總是需要配置的,如果程序分散部署在多臺機器上,要逐個改變配置就變得困難。現在把這些配置全部放到zookeeper上去,保存在 Zookeeper 的某個目錄節點中,然後所有相關應用程序對這個目錄節點進行監聽,一旦配置信息發生變化,每個應用程序就會收到 Zookeeper 的通知,然後從 Zookeeper 獲取新的配置信息應用到系統中就好
1.3.3集羣管理
所謂集羣管理無在乎兩點:是否有機器退出和加入、選舉master。
對於第一點,所有機器約定在父目錄GroupMembers下創建臨時目錄節點,然後監聽父目錄節點的子節點變化消息。一旦有機器掛掉,該機器與 zookeeper的連接斷開,其所創建的臨時目錄節點被刪除,所有其他機器都收到通知:某個兄弟目錄被刪除,於是,所有人都知道:它上船了。
新機器加入也是類似,所有機器收到通知:新兄弟目錄加入,highcount又有了,對於第二點,我們稍微改變一下,所有機器創建臨時順序編號目錄節點,每次選取編號最小的機器作爲master就好。
1.6Zookeeper設計目的
-
最終一致性:client不論連接到哪個Server,展示給它都是同一個視圖,這是zookeeper最重要的性能。
-
可靠性:具有簡單、健壯、良好的性能,如果消息被一臺服務器接受,那麼它將被所有的服務器接受。
-
實時性:Zookeeper保證客戶端將在一個時間間隔範圍內獲得服務器的更新信息,或者服務器失效的信息。但由於網絡延時等原因,Zookeeper不能保證兩個客戶端能同時得到剛更新的數據,如果需要最新數據,應該在讀數據之前調用
sync()接口
。 -
等待無關(wait-free):慢的或者失效的client不得干預快速的client的請求,使得每個client都能有效的等待。
-
原子性:更新只能成功或者失敗,沒有中間狀態。
-
順序性:包括全局有序和偏序兩種:全局有序是指如果在一臺服務器上消息a在消息b前發佈,則在所有Server上消息a都將在消息b前被髮布;偏序是指如果一個消息b在消息a後被同一個發送者發佈,a必將排在b前面。
1.7Zookeeper工作原理
Zookeeper 的核心是原子廣播,這個機制保證了各個Server之間的同步。實現這個機制的協議叫做Zab協議。Zab協議有兩種模式,它們分別是恢復模式(選主)和廣播模式(同步)。當服務啓動或者在領導者崩潰後,Zab就進入了恢復模式,當領導者被選舉出來,且大多數Server完成了和 leader的狀態同步以後,恢復模式就結束了。狀態同步保證了leader和Server具有相同的系統狀態。
爲了保證事務的順序一致性,zookeeper採用了遞增的事務id號(zxid)來標識事務。所有的提議(proposal)都在被提出的時候加上了zxid。實現中zxid是一個64位的數字,它高32位是epoch用來標識leader關係是否改變,每次一個leader被選出來,它都會有一個新的epoch,標識當前屬於那個leader的統治時期。低32位用於遞增計數。
2、zookeeper安裝
2.1下載與配置
http://mirrors.hust.edu.cn/apache/zookeeper 下載一個版本
解壓找到目錄conf 下創建 zoo.cfg 文件,默認就是加載這個文件,然後修改些東西
#zoo.cfg 的內容
# 心跳檢查的時間 2秒
tickTime=2000
# 初始化時 連接到服務器端的間隔次數,總時間10*2=20秒
initLimit=10
# ZK Leader 和follower 之間通訊的次數,總時間5*2=10秒
syncLimit=5
# 存儲內存中數據庫快照的位置,如果不設置參數,更新事務日誌將被存儲到默認位置。
dataDir=C:\\test\\tmp\\zookeeper
# 錯誤日誌的存放位置
dataLogDir=C:\\test\\log\\zookeeper
# ZK 服務器端的監聽端口
clientPort=2181
2.2啓動
進入到bin目錄,啓動,執行zkServer.cmd 就啓動成功了
3》客戶端添加節點代碼
public static final String ROOT = "/root"; //根節點
public static void main( String[] args ) throws Exception {
//1.創建一個與服務器的連接
ZooKeeper zooKeeper = new ZooKeeper("localhost:2181", 3000, new Watcher() {
//監控所有被觸發的事件
@Override
public void process(WatchedEvent event) {
System.out.println("狀態State:" + event.getState());
System.out.println("類型Type:" + event.getType());
System.out.println("Wrapper:" +event.getWrapper());
System.out.println("路徑Path:" + event.getPath());
}
});
//2.創建節點
//2.1 創建根節點,不控制權限,這裏需要用持久化節點,不然下面的節點創建容易出錯
//zooKeeper.create("/root","/root".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
//2.2 子節點,持久化順序編號目錄節點
// zooKeeper.create("/root/node1","/root/node1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
// zooKeeper.create("/root/node2","/root/node2".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
//2.3 子節點 臨時目錄節點 客戶端與zookeeper斷開連接後,該節點被刪除
//zooKeeper.create("/root/node3", "/root/node3".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
//2.4 子節點 session 過期自動刪除,也會加數字的後綴
//zooKeeper.create("/root/node4", "/root/node4".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
//3.查看節點
List<String> ktvs = zooKeeper.getChildren(ROOT, true);
System.out.println("所有子節點:" + Arrays.toString(ktvs.toArray()));
//4.刪除子節點
for (String nodeId : ktvs){
zooKeeper.delete(ROOT + "/" + nodeId,-1);
}
//刪除根節點
zooKeeper.delete(ROOT,-1);
zooKeeper.close();
}
依賴
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<log4j.version>1.2.17</log4j.version>
<slf4j.version>1.7.12</slf4j.version>