zookeeper從入門到放棄

前言

我們都知道zookeeper可以作爲dubbo的註冊中心,但是那只是zookeeper功能的冰山一角。

zookeeper的定位是一個分佈式應用程序的分佈式協調服務。它提供了一組簡單的原語,分佈式應用程序可以基於這些原語來實現用於同步(synchronization),配置維護(configuration maintenance)以及(groups)和命名(naming)的更高級別的服務。

本篇主要從理論實踐兩方面講解zookeeper從單機到集羣的相關知識

介紹

zookeeper有三大特點

  • 高性能
  • 高可用性
  • 嚴格有序訪問

zookeeper高性能意味着它可以在大型的分佈式系統中使用。高可用性使它不會成爲單點故障嚴格有序訪問意味着可以在客戶端上實現複雜的同步原語。

zookeeper提供了類似文件系統的目錄樹結構樣式的數據模型。與文件系統不同的是,zookeeper每一個節點都可以存儲數據。最大爲1M,數據是二進制安全的,存儲數據少是爲了保證zookeeper的高性能。zookeeper的數據模型如下
zookeeper
(圖片來源:http://zookeeper.apache.org/doc/current/zookeeperOver.html)

zookeeper的節點也叫Znode,分爲臨時節點持久節點

  • 臨時節點:生命週期和session相同,session消失時,zookeeper會自動刪除臨時節點
  • 持久節點:持久化到硬盤,不會隨着session的消失而被刪除

瞭解了關於zookeeper的基礎知識後,就可以開始安裝zookeeper了。

實驗環境

  • VMware Workstation 15
  • CentOS Linux release 7.7.1908
  • zookeeper-3.4.14
  • jdk-8u144-linux-x64.tar.gz

注意事項

  • 文章後面集羣部分四個節點ip分別爲192.168.1.101192.168.1.102192.168.1.103192.168.1.104
  • 確保四個節點都安裝了JDK1.8,並且配置好了環境變量
  • 確保四個節點能夠相互通信
  • 確保Linux的wgettar等基礎命令可用
  • 建議先關閉防火牆,Centos 7操作如下
    firewall-cmd --state ## 查看防火牆狀態 not running表示已經關閉
    systemctl stop firewalld.service ## 關閉防火牆
    systemctl disable firewalld.service ## 禁止開機啓動防火牆
    

單機安裝

  • 下載

    wget https://downloads.apache.org/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
    
  • 解壓

    tar -zxvf zookeeper-3.4.14.tar.gz
    
  • 配置環境變量

    vim /etc/profile
    

    配置內容如下

    export ZOOKEEPER_HOME=/root/zookeeper-3.4.14
    export PATH=$PATH:$ZOOKEEPER_HOME/bin
    

    刷新配置文件

    source /etc/profile
    
  • 修改zookeeper配置文件

    cd zookeeper-3.4.14/conf/
    
    ## zookeeper默認加載的是zoo.cfg文件
    cp zoo_sample.cfg zoo.cfg
    
    ## 創建zookeeper數據存放目錄
    mkdir /var/lib/zookeeper
    
    ## 編輯配置
    vim zoo.cfg
    

    zookeeper的配置文件參數比較少,基本上需要修改只有數據目錄

    # the directory where the snapshot is stored.
    # do not use /tmp for storage, /tmp here is just 
    # example sakes.
    dataDir=/var/lib/zookeeper
    
  • 啓動

    ## start 表示後臺啓動
    zkServer.sh start
    ## start-foreground表示在前臺啓動
    zkServer.sh start-foreground
    ## 其它啓動參數參考命令
    zkServer.sh help
    
  • 查看狀態
    zkServer.sh status可以查看zookeeper服務的狀態

    [root@localhost ~]# zkServer.sh status
    ZooKeeper JMX enabled by default
    Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg
    Mode: standalone
    

單節點的安裝,很容易就完成了。

操作

zkCli.sh客戶端可以連接zookeeper服務,直接運行即可,默認連接本機的zookeeper服務。連接成功後輸入help可以看到zookeeper服務提供的功能。

[zk: localhost:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args
	stat path [watch]
	set path data [version]
	ls path [watch]
	delquota [-n|-b] path
	ls2 path [watch]
	setAcl path acl
	setquota -n|-b val path
	history 
	redo cmdno
	printwatches on|off
	delete path [version]
	sync path
	listquota path
	rmr path
	get path [watch]
	create [-s] [-e] path data acl
	addauth scheme auth
	quit 
	getAcl path
	close 
	connect host:port

命令比較簡單,常用的就是creatermrsetls,也就是增刪改查。

需要注意的是create [-s] [-e] path data acl命令中data參數是必須的,如果不想寫入數據,可以寫空字符串""

[zk: localhost:2181(CONNECTED) 2] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 3] create sicimike   
[zk: localhost:2181(CONNECTED) 4] ls /            
[zookeeper]
[zk: localhost:2181(CONNECTED) 5] create sicimike ""
Command failed: java.lang.IllegalArgumentException: Path must start with / character
[zk: localhost:2181(CONNECTED) 6] create /sicimike ""
Created /sicimike
[zk: localhost:2181(CONNECTED) 7] ls /
[sicimike, zookeeper]

上述操作可以看到兩點

  • 如果不加data參數是無法創建Znode
  • 路徑必須以/開始
[zk: localhost:2181(CONNECTED) 8] create /sicimike/sicimike-1 "hello world"
Created /sicimike/sicimike-1
[zk: localhost:2181(CONNECTED) 9] get /sicimike/sicimike-1
hello world
cZxid = 0x3
ctime = Mon Apr 27 22:33:13 CST 2020
mZxid = 0x3
mtime = Mon Apr 27 22:33:13 CST 2020
pZxid = 0x3
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 0

從上述操作可以看出兩點

  • Znode下面可以繼續創建Znode
  • Znode中不僅存了命令寫入的數據hello world,還存放了一些元數據。其中ephemeralOwner表示臨時節點被哪個session持有。此處爲0x0,表示創建的是持久節點。

在用zkCli.sh連接服務的時候,會有這樣一行日誌

2020-04-28 20:18:11,225 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server localhost/0:0:0:0:0:0:0:1:2181, sessionid = 0x100005d814d0002, negotiated timeout = 30000

從日誌中可以看到,本次連接的sessionid0x100005d814d0002。連接之後,創建一個臨時節點

[zk: localhost:2181(CONNECTED) 1] create -e /temp-sicimike "hello world"
Created /temp-sicimike
[zk: localhost:2181(CONNECTED) 2] get /temp-sicimike
hello world
cZxid = 0xe
ctime = Tue Apr 28 20:18:45 CST 2020
mZxid = 0xe
mtime = Tue Apr 28 20:18:45 CST 2020
pZxid = 0xe
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x100005d814d0002
dataLength = 11
numChildren = 0

create -e表示創建一個臨時節點,創建後查看節點,ephemeralOwner就變成了sessionid

客戶端退出(命令quit)時,session斷開,臨時節點也會被刪除。

單節點的安裝和操作都非常簡單,接下來進行集羣的搭建。

集羣搭建

zookeeper的集羣是一主多從模式。

zookeeper集羣
(圖片來源:http://zookeeper.apache.org/doc/current/zookeeperOver.html)

集羣結構如圖所示,集羣中的節點分爲三種角色:leaderfollowerobserver

  • leader就是主節點,最多隻有一個
  • follower是從節點,可以有多個
  • observer也是從節點,可以有多個,和follower唯一的區別就是不參與選舉過程。也就是當leader故障之後,follower重新選出一個leaderobserver不會參與這個過程

客戶端可以連接任意節點,但是會把寫操作交給leader去執行,再由leader把數據同步給所有的follower,以達到數據一致性。

zookeeper服務可以提供一組保證

  • 順序一致性(Sequential Consistency):客戶端發送的所有命令都會按順序執行
  • 原子性(Atomicity ):更新成功或失敗。沒有部分成功。
  • 單個系統映像(Single System Image):無論客戶端連接到哪個節點,客戶端都將看到相同的服務視圖
  • 可靠性(Reliability ):應用更新後,此更新會被持久化。
  • 及時性(Timeliness ):確保系統的客戶視圖在特定時間範圍內是最新的。

瞭解了zookeeper集羣相關的知識後,就可以開始搭建zookeeper集羣了。

集羣的搭建起來非常簡單,只需要稍微改寫配置文件即可。

修改zoo.cfg文件,四個節點配置內容相同,均配置如下信息

# The number of milliseconds of each tick
## 每次心跳的超時時間,單位毫秒
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
## leader和follower之間初始連接的最大心跳次數 initLimit * tickTime 就是最大時間
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
## leader和follower之間通信的最多能忍受多少次心跳,超時的follower會被踢除集羣 syncLimit * tickTime 就是最大時間
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/var/lib/zookeeper

## zookeeper的集羣需要手動規劃
server.1=192.168.1.101:2888:3888
server.2=192.168.1.102:2888:3888
server.3=192.168.1.103:2888:3888
server.4=192.168.1.104:2888:3888

每個ip配置了2個端口,其中3888端口的作用是:集羣第一次啓動尚未選出leader,或者leader已經出故障時,節點之間通過3888端口通信,選出leader。選出leader後,其餘的節點都會連接leader2888端口進行通信。

記得在每個節點上創建/var/lib/zookeeper目錄,並且在該目錄下新建文件,寫入自己的服務id

## 節點1 192.168.1.101
echo 1 > myid

## 節點2 192.168.1.103
echo 2 > myid

## 節點3 192.168.1.103
echo 3 > myid

## 節點4 192.168.1.104
echo 4 > myid

配置好了之後,依次啓動四個節點。

此時再看節點狀態

[root@localhost ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg
Mode: leader
[root@localhost ~]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /root/zookeeper-3.4.14/bin/../conf/zoo.cfg
Mode: follower

有一個已經變成了leader,其餘的成了follower

客戶端連接任意一個節點,都是都是可以寫入的。其實是follower節點會把寫入命令發送給leader去執行,follower節點只負責讀操作

之前在查看節點數據的時候,Znode節點中存儲數據的格式如下

[zk: localhost:2181(CONNECTED) 6] get /sicimike
hello
cZxid = 0x100000007
ctime = Tue Apr 28 22:03:51 CST 2020
mZxid = 0x100000007
mtime = Tue Apr 28 22:03:51 CST 2020
pZxid = 0x100000007
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

除了客戶端存入的數據hello,之外,還存儲了一些元數據。

  • cZxid:是一個64位的二進制數,高32位表示leader的紀元,低32位表示創建的事務ID。因爲zookeeper是可以保證順序一致性的,所以的寫操作都由leader來完成,所以寫操作會有一個遞增的事務ID
  • ctime:Znode創建時間
  • mZxid:和cZxid一樣,高32位表示leader的紀元,低32位表示修改的事務ID
  • mtime:Znode修改時間
  • pZxid:高32位表示leader的紀元,低32位表示當前Znode下,創建最後一個子Znode的事務ID

在創建Znode的時候,可以看到create命令的結構如下

create [-s] [-e] path data acl

除了可加參數-e表示臨時節點,還可以加參數-s,操作命令如下

[zk: localhost:2181(CONNECTED) 7] create -s /sicimike "hello world"
Created /sicimike0000000001
[zk: localhost:2181(CONNECTED) 8] create -s /sicimike "hello world 1"
Created /sicimike0000000002
[zk: localhost:2181(CONNECTED) 9] create -s /sicimike "hello world 3"
Created /sicimike0000000003
[zk: localhost:2181(CONNECTED) 10] ls /
[sicimike0000000002, sicimike0000000003, sicimike, zookeeper, sicimike0000000001]
[zk: localhost:2181(CONNECTED) 11] get /sicimike0000000001
hello world
cZxid = 0x100000008
ctime = Tue Apr 28 22:20:27 CST 2020
mZxid = 0x100000008
mtime = Tue Apr 28 22:20:27 CST 2020
pZxid = 0x100000008
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 11
numChildren = 0

可以看到了-s參數後,創建的Znode都帶有數字,並且是一個遞增的數字。實際上-ssequence(序列)的意思,就是創建帶有序列的Znode

帶序列的Znode可以用於以下場景,當多個客戶端想要寫入同一個路徑(path),而又不想相互覆蓋的時候就可以使用帶序列的節點。上面的操作中path都是一樣的,但是Znode沒有相互覆蓋。

前文中說Znode可以分爲臨時節點持久節點,這裏又可以分成非序列節點序列節點。根據排列組合可以知道,zookeeper的Znode可以分成四種:臨時序列節點臨時非序列節點持久非序列節點持久序列節點

連接狀態

前文配置可以看到,集羣節點之間通過2888和3888兩個端口通信,因此可以通過查看這兩個端口的連接狀態開觀察集羣的連接情況。操作命令如下

netstat -natp |egrep ('2888'|'3888')

四個集羣節點的連接信息分別如下(博主搭建的集羣中,192.168.1.104leader

  • 192.168.101
    tcp6       0      0 192.168.1.101:3888      :::*                    LISTEN      1253/java           
    tcp6       0      0 192.168.1.101:3888      192.168.1.102:43972     ESTABLISHED 1253/java           
    tcp6       0      0 192.168.1.101:3888      192.168.1.104:45424     ESTABLISHED 1253/java           
    tcp6       0      0 192.168.1.101:3888      192.168.1.103:56906     ESTABLISHED 1253/java           
    tcp6       0      0 192.168.1.101:59638     192.168.1.104:2888      ESTABLISHED 1253/java
    
  • 192.168.102
    tcp6       0      0 192.168.1.102:3888      :::*                    LISTEN      1179/java           
    tcp6       0      0 192.168.1.102:43972     192.168.1.101:3888      ESTABLISHED 1179/java           
    tcp6       0      0 192.168.1.102:57100     192.168.1.104:2888      ESTABLISHED 1179/java           
    tcp6       0      0 192.168.1.102:3888      192.168.1.103:56180     ESTABLISHED 1179/java           
    tcp6       0      0 192.168.1.102:3888      192.168.1.104:35646     ESTABLISHED 1179/java 
    
  • 192.168.103
    tcp6       0      0 192.168.1.103:3888      :::*                    LISTEN      1328/java           
    tcp6       0      0 192.168.1.103:36788     192.168.1.104:2888      ESTABLISHED 1328/java           
    tcp6       0      0 192.168.1.103:56180     192.168.1.102:3888      ESTABLISHED 1328/java           
    tcp6       0      0 192.168.1.103:3888      192.168.1.104:37652     ESTABLISHED 1328/java           
    tcp6       0      0 192.168.1.103:56906     192.168.1.101:3888      ESTABLISHED 1328/java
    
  • 192.168.104
    tcp6       0      0 192.168.1.104:2888      :::*                    LISTEN      1184/java           
    tcp6       0      0 192.168.1.104:3888      :::*                    LISTEN      1184/java           
    tcp6       0      0 192.168.1.104:2888      192.168.1.103:36788     ESTABLISHED 1184/java           
    tcp6       0      0 192.168.1.104:35646     192.168.1.102:3888      ESTABLISHED 1184/java           
    tcp6       0      0 192.168.1.104:45424     192.168.1.101:3888      ESTABLISHED 1184/java           
    tcp6       0      0 192.168.1.104:37652     192.168.1.103:3888      ESTABLISHED 1184/java           
    tcp6       0      0 192.168.1.104:2888      192.168.1.101:59638     ESTABLISHED 1184/java           
    tcp6       0      0 192.168.1.104:2888      192.168.1.102:57100     ESTABLISHED 1184/java
    

根據信息可以知道四個節點連接情況如下
zookeeper集羣
綠色的線表示3888端口,也就是用來選出leader的端口,而紅色的線是2888端口,是用來leader和follower之間同步指令的端口。

有了連接信息,就能更好的瞭解zookeeper集羣的工作原理。

總結

zookeeper單機和集羣的搭建都比較簡單,重要的是理解zookeeper的內部結構。對zookeeper有個整體的認識,這將有助於我們更好的使用zookeeper。

參考

  • http://zookeeper.apache.org/doc/current/zookeeperOver.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章