Zookeeper集群搭建以及python操作zk


一、Zookeeper原理簡介

ZooKeeper是一個開放源碼的分布式應用程序協調服務,它包含一個簡單的原語集,分布式應用程序可以基於它實現同步服務,配置維護和命名服務等。

 

Zookeeper設計目的

  • 最終一致性:client不論連接到那個Server,展示給它的都是同一個視圖。
  • 可靠性:具有簡單、健壯、良好的性能、如果消息m被到一台服務器接收,那么消息m將被所有服務器接收。
  • 實時性:Zookeeper保證客戶端將在一個時間間隔范圍內獲得服務器的更新信息,或者服務器失效的信息。但由於網絡延時等原因,Zookeeper不能保證兩個客戶端能同時得到剛更新的數據,如果需要最新數據,應該在讀數據之前調用sync()接口。
  • 等待無關(wait-free):慢的或者失效的client不得干預快速的client的請求,使得每個client都能有效的等待。
  • 原子性:更新只能成功或者失敗,沒有中間狀態。
  • 順序性:包括全局有序和偏序兩種:全局有序是指如果在一台服務器上消息a在消息b前發布,則在所有Server上消息a都將在消息b前被發布;偏序是指如果一個消息b在消息a后被同一個發送者發布,a必將排在b前面。

Zookeeper工作原理

1、在zookeeper的集群中,各個節點共有下面3種角色和4種狀態:

角色:leader,follower,observer
狀態:leading,following,observing,looking

Zookeeper的核心是原子廣播,這個機制保證了各個Server之間的同步。實現這個機制的協議叫做Zab協議(ZooKeeper Atomic Broadcast protocol)。Zab協議有兩種模式,它們分別是恢復模式(Recovery選主)和廣播模式(Broadcast同步)。當服務啟動或者在領導者崩潰后,Zab就進入了恢復模式,當領導者被選舉出來,且大多數Server完成了和leader的狀態同步以后,恢復模式就結束了。狀態同步保證了leader和Server具有相同的系統狀態。

為了保證事務的順序一致性,zookeeper采用了遞增的事務id號(zxid)來標識事務。所有的提議(proposal)都在被提出的時候加上了zxid。實現中zxid是一個64位的數字,它高32位是epoch用來標識leader關系是否改變,每次一個leader被選出來,它都會有一個新的epoch,標識當前屬於那個leader的統治時期。低32位用於遞增計數。

每個Server在工作過程中有4種狀態:

LOOKING:當前Server不知道leader是誰,正在搜尋。

LEADING:當前Server即為選舉出來的leader。

FOLLOWING:leader已經選舉出來,當前Server與之同步。

OBSERVING:observer的行為在大多數情況下與follower完全一致,但是他們不參加選舉和投票,而僅僅接受(observing)選舉和投票的結果。

Zookeeper集群節點

  • Zookeeper節點部署越多,服務的可靠性越高,建議部署奇數個節點,因為zookeeper集群是以宕機個數過半才會讓整個集群宕機的。
  • 需要給每個zookeeper 1G左右的內存,如果可能的話,最好有獨立的磁盤,因為獨立磁盤可以確保zookeeper是高性能的。如果你的集群負載很重,不要把zookeeper和RegionServer運行在同一台機器上面,就像DataNodes和TaskTrackers一樣。
 

實驗環境

操作系統 docker鏡像 docker ip zookeeper版本
ubuntu-16.04.5-server-amd64 ubuntu:16.04 172.168.0.2 3.4.13
ubuntu-16.04.5-server-amd64 ubuntu:16.04 172.168.0.2 3.4.13
ubuntu-16.04.5-server-amd64 ubuntu:16.04 172.168.0.2 3.4.13

 

 

 

 

 

由於機器有限,本文在一台服務器上面,開啟了3個docker容器。

使用網橋連接3個docker容器。這樣,就可以模擬3台服務器了!

 

創建網橋

bridge有以下好處:

  1. 好的隔離性和互操作性:連到同一自定義的bridge的各個容器默認相互之間曝露所有端口,並且不對外部曝露

  2. 自動提供容器之間的DNS解析服務:連到同一自定義的bridge的各個容器不用做特殊DNS配置,可直接通過hostname訪問

  3. 運行中容器聯網配置:可對運行中的容器配置自定義或取消配置自定義bridge

  4. bridge之間相互獨立:用戶可創建N多個bridge,且連接於不同的bridge之上的容器相互獨立

 

創建自定義bridge,名字叫br1

docker network create --driver=bridge --subnet=172.168.0.0/16 br1

 

語法解釋:

--driver=bridge 表示使用橋接模式

--subnet 表示網絡地址

 

為容器配置靜態IP,可以使用以下命令

docker run -it --network my-net --ip 172.18.0.250 imageID bash

 

Zookeeper核心要點

1. Zookeeper節點必須是奇數

2. 修改zoo.cfg

末尾增加3行參數。表示有3個zk節點!

server.X=A:B:C
server.X=A:B:C
server.X=A:B:C

 

官方解釋

The entries of the form server.X list the servers that make up the ZooKeeper service. When the server starts up, it knows which server it is by looking for the file myid in the data directory. That file has the contains the server number, in ASCII.

Finally, note the two port numbers after each server name: " 2888" and "3888". Peers use the former port to connect to other peers. Such a connection is necessary so that peers can communicate, for example, to agree upon the order of updates. More specifically, a ZooKeeper server uses this port to connect followers to the leader. When a new leader arises, a follower opens a TCP connection to the leader using this port. Because the default leader election also uses TCP, we currently require another port for leader election. This is the second port in the server entry.
View Code

 

蹩腳翻譯

表單server.X的條目列出構成ZooKeeper服務的服務器。當服務器啟動時,它通過查找數據目錄中的文件myid來知道它是哪個服務器 。該文件包含服務器編號,以ASCII格式顯示。

最后,請注意每個服務器名稱后面的兩個端口號:“2888”和“3888”。對等體使用前端口連接到其他對等體。這樣的連接是必要的,使得對等體可以進行通信,例如,以商定更新的順序。更具體地說,一個ZooKeeper服務器使用這個端口來連接追隨者到領導者。當新的領導者出現時,追隨者使用此端口打開與領導者的TCP連接。因為默認領導選舉也使用TCP,所以我們目前需要另外一個端口進行領導選舉。這是服務器條目中的第二個端口。
View Code

 

大概意思

server.X=A:B:C

X-代表服務器編號

A-代表ip

B和C-代表端口,這個端口用來系統之間通信

 

3. 創建ServerID標識

除了修改zoo.cfg配置文件外,zookeeper集群模式下還要配置一個myid文件,這個文件需要放在dataDir目錄下。

這個文件里面有一個數據就是A的值(該A就是zoo.cfg文件中server.A=B:C:D中的A),在zoo.cfg文件中配置的dataDir路徑中創建myid文件

 

二、Zookeeper安裝

Zookeeper運行需要java環境,需要安裝jdk,注:每台服務器上面都需要安裝zookeeper、jdk。

基於docker安裝

新建空目錄

mkdir /opt/zookeeper_cluster

 

dockerfile

FROM ubuntu:16.04
# 修改更新源為阿里雲
ADD sources.list /etc/apt/sources.list
ADD zookeeper-3.4.13.tar.gz /
ADD zoo.cfg / 
# 安裝jdk
RUN apt-get update && apt-get install -y openjdk-8-jdk --allow-unauthenticated && apt-get clean all && \ 
    cd /zookeeper-3.4.13 && \
    mkdir data log && \
    mv /zoo.cfg conf

EXPOSE 2181
# 添加啟動腳本
ADD run.sh .
RUN chmod 755 run.sh
ENTRYPOINT [ "/run.sh"]

 

run.sh

#!/bin/bash

if [ -z "${SERVER_ID}" ];then
    echo "SERVER_ID 變量缺失"
    exit 1
fi

if [ -z "${ZOOKEEPER_CONNECT}" ];then
        echo "ZOOKEEPER_CONNECT環境變量缺失"
        echo "比如: 192.168.1.1,192.168.1.2,192.168.1.3"
        exit 2
fi

# 配置集群
# 寫入server.X對應的X
echo $SERVER_ID > /zookeeper-3.4.13/data/myid

# 寫入文件zoo.cfg
id=0  # 初始值
hosts_array=(${ZOOKEEPER_CONNECT//,/ })  # 以逗號來切割,轉換為數組
for ip in ${hosts_array[@]};do
    ((id++))  # id自增1
    echo "server.$id=$ip:2888:3888" >> /zookeeper-3.4.13/conf/zoo.cfg
done

# 啟動zookeeper
cd /zookeeper-3.4.13/
bin/zkServer.sh start

tail -f /zookeeper-3.4.13/conf/zoo.cfg

 

注意:此腳本需要2個變量,否則會直接退出。它會在zoo.cfg寫入3行內容!

除了修改zoo.cfg配置文件外,zookeeper集群模式下還要配置一個myid文件,這個文件需要放在dataDir目錄下

/zookeeper-3.4.13/data/myid 這個文件的數值,待會由docker啟動時,傳入進去。

 

 sources.list

deb http://mirrors.aliyun.com/ubuntu/ xenial main
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main

deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main

deb http://mirrors.aliyun.com/ubuntu/ xenial universe
deb-src http://mirrors.aliyun.com/ubuntu/ xenial universe
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates universe

deb http://mirrors.aliyun.com/ubuntu/ xenial-security main
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main
deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security universe

 

zoo.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/zookeeper-3.4.13/data
dataLogDir=/zookeeper-3.4.13/log
clientPort=2181

 

initLimit 這個配置項是用來配置zookeeper接受客戶端(這里所說的客戶端不是用戶連接zookeeper服務器的客戶端,而是zookeeper服務器集群中連接到leader的follower 服務器)初始化連接時最長能忍受多少個心跳時間間隔數。

syncLimit 這個配置項標識leader與follower之間發送消息,請求和應答時間長度,最長不能超過多少個tickTime的時間長度,總的時間長度就是5*2000=10秒。

注意:上面的2行紅色部分參數,一定要加。否則會導致啟動zookeeper失敗,因為啟動時,它會嘗試連接server.X=A:B:C。有多少個,都會連接。

如果連接失敗數大於集群總數一半,會認為集群不可用。

那么配置這2個參數之后,就會有時間緩解一下。因為我不是閃電俠,我不能在一瞬間同時啟動3個zookeeper。而且你還得確保,zookeeper檢查其他節點時,其他節點運行是正常的!

因此,只要你在10秒內,啟動3個zookeeper,就可以了!

 

此時,目錄結構如下:

./
├── dockerfile
├── run.sh
├── sources.list
├── zoo.cfg
└── zookeeper-3.4.13.tar.gz

 

生成鏡像

docker build -t zookeeper_cluster /opt/zookeeper_cluster

 

啟動docker

在啟動之前,請確保已經創建了網橋br1

 

啟動第一個docker

docker run -it -e SERVER_ID=1 -e ZOOKEEPER_CONNECT=172.168.0.2,172.168.0.3,172.168.0.4 -p 2181:2181 --network br1 --ip=172.168.0.2 zookeeper_cluster

 

啟動第二個docker

docker run -it -e SERVER_ID=2 -e ZOOKEEPER_CONNECT=172.168.0.2,172.168.0.3,172.168.0.4 -p 2182:2181 --network br1 --ip=172.168.0.3 zookeeper_cluster

 

啟動第二個docker

docker run -it -e SERVER_ID=3 -e ZOOKEEPER_CONNECT=172.168.0.2,172.168.0.3,172.168.0.4 -p 2183:2181 --network br1 --ip=172.168.0.4 zookeeper_cluster

 

注意紅色部分,是需要修改的,其他的參數都是一樣的!

三、Zookeeper集群查看

查看每個節點狀態

先來查看一下docker進程

root@jqb-node128:/opt/zookeeper_cluster# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
217e012c9566        3f3a8090dcb6        "/run.sh"           15 hours ago        Up 15 hours         0.0.0.0:2183->2181/tcp   gallant_golick
3b4861d2fef9        3f3a8090dcb6        "/run.sh"           15 hours ago        Up 15 hours         0.0.0.0:2182->2181/tcp   jovial_murdock
ed91c1f973d2        3f3a8090dcb6        "/run.sh"           15 hours ago        Up 15 hours         0.0.0.0:2181->2181/tcp   dazzling_hamilton

 

查看每個節點狀態,使用命令 zkServer.sh status 查看

root@jqb-node128:~# docker exec -it 217e012c9566 /zookeeper-3.4.13/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /zookeeper-3.4.13/bin/../conf/zoo.cfg
Mode: follower
root@jqb-node128:~# docker exec -it 3b4861d2fef9 /zookeeper-3.4.13/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /zookeeper-3.4.13/bin/../conf/zoo.cfg
Mode: leader
root@jqb-node128:~# docker exec -it ed91c1f973d2 /zookeeper-3.4.13/bin/zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /zookeeper-3.4.13/bin/../conf/zoo.cfg
Mode: follower

 

可以發現,第二台,也就是id為3b4861d2fef9 的容器,就是leader模式。其它容器是follow模式

 

四、Zookeeper集群連接

 Zookeeper集群搭建完畢之后,可以通過客戶端腳本連接到zookeeper集群上面,對客戶端來說,zookeeper集群是一個整體,連接到zookeeper集群實際上感覺在獨享整個集群的服務。

 

指定第一台節點

docker exec -it 217e012c9566 /zookeeper-3.4.13/bin/zkCli.sh -server 172.168.0.2:2181

 

使用ls / 查看根節點,默認只有一個zookeeper節點。

WatchedEvent state:SyncConnected type:None path:null
[zk: 172.168.0.2:2181(CONNECTED) 0] ls /
[zookeeper]
[zk: 172.168.0.2:2181(CONNECTED) 1] 

 

五、使用python操作zookeeper

kazoo 介紹

zookeeper的開發接口以前主要以java和c為主,隨着python項目越來越多的使用zookeeper作為分布式集群實現,python的zookeeper接口也出現了很多,現在主流的純python的zookeeper接口是kazoo。因此如何使用kazoo開發基於python的分布式程序是必須掌握的。

 

安裝kazoo

pip3 install kazoo

 

基本操作

from kazoo.client import KazooClient
zk = KazooClient(hosts='192.168.91.128:2181')    #如果是本地那就寫127.0.0.1
zk.start()    #與zookeeper連接
zk.stop()    #與zookeeper斷開

 

創建節點

from kazoo.client import KazooClient
zk = KazooClient(hosts='192.168.91.128:2181')    #如果是本地那就寫127.0.0.1
zk.start()    #與zookeeper連接
#makepath=True是遞歸創建,如果不加上中間那一段,就是建立一個空的節點
zk.create('/abc/JQK/XYZ/0001',b'this is my house',makepath=True)
node = zk.get_children('/')  # 查看根節點有多少個子節點
print(node)
zk.stop()    #與zookeeper斷開

 

執行輸出:

['abc', 'zookeeper']

 

注意:空節點的值不能用set修改,否則執行報錯!

 

刪除節點

如果要刪除這個/abc/JQK/XYZ/0001的子node,但是想要上一級XYZ這個node還是存在的,語句如下:

from kazoo.client import KazooClient
zk = KazooClient(hosts='192.168.91.128:2181')    #如果是本地那就寫127.0.0.1
zk.start()    #與zookeeper連接
#recursive=True是遞歸刪除,就是無視下面的節點是否是空,都干掉,不加上的話,會提示子節點非空,刪除失敗
zk.delete('/abc/JQK/XYZ/0001',recursive=True)
node = zk.get_children('/')  # 查看根節點有多少個子節點
print(node)
zk.stop()    #與zookeeper斷開

 

執行輸出:

['abc', 'zookeeper']

 

更改節點

現在假如要在0001這個node里更改value,比如改成:"this is my horse!",

由於上面節點已經被刪除掉了,需要先創建一次。

語句如下:

from kazoo.client import KazooClient
zk = KazooClient(hosts='192.168.91.128:2181')    #如果是本地那就寫127.0.0.1
zk.start()    #與zookeeper連接
zk.create('/abc/JQK/XYZ/0001',b'this is my house',makepath=True)
zk.set('/abc/JQK/XYZ/0001',b"this is my horse!")
node = zk.get('/abc/JQK/XYZ/0001')  # 查看值
print(node)
zk.stop()    #與zookeeper斷開

 

執行輸出:

(b'this is my horse!', ZnodeStat(czxid=4294967449, mzxid=4294967452, ctime=1544598301273, mtime=1544598308267, version=1, cversion=0, aversion=0, ephemeralOwner=0, dataLength=17, numChildren=0, pzxid=4294967449))

 

注意!set這種增加節點內容的方式是覆蓋式增加,並不是在原有基礎上增添。而且添加中文的話可能在ZooInspecter里出現的是亂碼

 

查看節點

由於所有節點,都是在/ 節點上面的,直接查看根節點,就可以知道所有節點了

from kazoo.client import KazooClient
zk = KazooClient(hosts='192.168.91.128:2181')    #如果是本地那就寫127.0.0.1
zk.start()    #與zookeeper連接
node = zk.get_children('/')
print(node)
zk.stop()    #與zookeeper斷開

 

執行輸出:

['abc', 'zookeeper']

 

一鍵清空zookeeper

有些時候,需要將zookeeper的數據全部清空,可以使用以下代碼

from kazoo.client import KazooClient
zk = KazooClient(hosts='192.168.91.128:2181')    #如果是本地那就寫127.0.0.1
zk.start()    #與zookeeper連接
jiedian = zk.get_children('/')  # 查看根節點有多少個子節點
print(jiedian)
for i in jiedian:
    if i != 'zookeeper':  # 判斷不等於zookeeper
        print(i)
        # 刪除節點
        zk.delete('/%s'%i,recursive=True)
zk.stop()    #與zookeeper斷開

 

注意:默認的zookeeper節點,是不允許刪除的,所以需要做一個判斷。

 

 

 

本文參考鏈接:

https://www.cnblogs.com/linuxprobe/p/5851699.html

http://www.cnblogs.com/LUA123/p/7222216.html

http://blog.51cto.com/chenx1242/2053627

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM