這裏是 Docker 實戰系列第四篇。主要介紹分佈式系統中的元老級組件 Zookeeper。node
ZooKeeper 是一個開源的分佈式協調服務,是 Hadoop,HBase 和其餘分佈式框架使用的有組織服務的標準。算法
分佈式應用程序能夠基於 ZooKeeper 實現諸如數據發佈/訂閱、負載均衡、命名服務、分佈式協調/通知、集羣管理、Master 選舉、分佈式鎖和分佈式隊列等功能。docker
讀過 Docker 實戰之 Consul 集羣 的小夥伴應該有印象,裏邊有一張一致性算法的對比圖。全部的分佈式系統都面臨着 CAP 理論的抉擇,都須要一致性算法的保障。這裏先放上一個簡單的總結,用於你們借鑑那些頂級開源軟件在分佈式上的思路。bash
分佈式組件 | 算法/協議 | 服務 |
---|---|---|
Redis Cluster | Gossip | master 提供讀寫,slave 只備份 |
Zookeeper | ZAB | Leader 提供讀寫,Follower 只讀,遇到寫請求轉發給 Leader |
Kafka | ZK 臨時節點 | 只有 leader 提供讀寫服務 |
大體來講,zookeeper 的使用場景以下:負載均衡
這裏引用中華石杉老師的例子框架
這個實際上是 zookeeper 很經典的一個用法,簡單來講,就比如,你 A 系統發送個請求到 mq,而後 B 系統消息消費以後處理了。那 A 系統如何知道 B 系統的處理結果?用 zookeeper 就能夠實現分佈式系統之間的協調工做。A 系統發送請求以後能夠在 zookeeper 上對某個節點的值註冊個監聽器,一旦 B 系統處理完了就修改 zookeeper 那個節點的值,A 系統立馬就能夠收到通知,完美解決。less
舉個栗子。對某一個數據連續發出兩個修改操做,兩臺機器同時收到了請求,可是隻能一臺機器先執行完另一個機器再執行。那麼此時就可使用 zookeeper 分佈式鎖,一個機器接收到了請求以後先獲取 zookeeper 上的一把分佈式鎖,就是能夠去建立一個 znode,接着執行操做;而後另一個機器也嘗試去建立那個 znode,結果發現本身建立不了,由於被別人建立了,那隻能等着,等第一個機器執行完了本身再執行。分佈式
zookeeper 能夠用做不少系統的配置信息的管理,好比 kafka、storm 等等不少分佈式系統都會選用 zookeeper 來作一些元數據、配置信息的管理,包括 dubbo 註冊中心不也支持 zookeeper 麼?oop
這個應該是很常見的,好比 hadoop、hdfs、yarn 等不少大數據系統,都選擇基於 zookeeper 來開發 HA 高可用機制,就是一個重要進程通常會作主備兩個,主進程掛了立馬經過 zookeeper 感知到切換到備用進程。測試
docker-compose-zookeeper-cluster.yml
version: '3.7' networks: docker_net: external: true services: zoo1: image: zookeeper restart: unless-stopped hostname: zoo1 container_name: zoo1 ports: - 2182:2181 environment: ZOO_MY_ID: 1 ZOO_SERVERS: server.1=0.0.0.0:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181 volumes: - ./zookeeper/zoo1/data:/data - ./zookeeper/zoo1/datalog:/datalog networks: - docker_net zoo2: image: zookeeper restart: unless-stopped hostname: zoo2 container_name: zoo2 ports: - 2183:2181 environment: ZOO_MY_ID: 2 ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=0.0.0.0:2888:3888;2181 server.3=zoo3:2888:3888;2181 volumes: - ./zookeeper/zoo2/data:/data - ./zookeeper/zoo2/datalog:/datalog networks: - docker_net zoo3: image: zookeeper restart: unless-stopped hostname: zoo3 container_name: zoo3 ports: - 2184:2181 environment: ZOO_MY_ID: 3 ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=0.0.0.0:2888:3888;2181 volumes: - ./zookeeper/zoo3/data:/data - ./zookeeper/zoo3/datalog:/datalog networks: - docker_net
啓動集羣
docker-compose -f docker-compose-zookeeper-cluster.yml up -d
在 ZAB 算法中,存在 Leader、Follower、Observer 三種角色,如今咱們就來認識下它們。
➜ docker docker exec -it zoo1 /bin/sh # zkServer.sh status ZooKeeper JMX enabled by default Using config: /conf/zoo.cfg Client port found: 2181. Client address: localhost. Mode: follower
由上結果可知,zoo1 是 follower
➜ docker docker exec -it zoo2 /bin/sh # zkServer.sh status ZooKeeper JMX enabled by default Using config: /conf/zoo.cfg Client port found: 2181. Client address: localhost. Mode: follower
由上結果可知,zoo2 是 follower
➜ docker docker exec -it zoo3 /bin/sh # zkServer.sh status ZooKeeper JMX enabled by default Using config: /conf/zoo.cfg Client port found: 2181. Client address: localhost. Mode: leader
由上結果可知,zoo3 是 leader。負責集羣的讀寫。
➜ docker echo srvr | nc localhost 2184 Zookeeper version: 3.5.6-c11b7e26bc554b8523dc929761dd28808913f091, built on 10/08/2019 20:18 GMT Latency min/avg/max: 0/0/0 Received: 2 Sent: 1 Connections: 1 Outstanding: 0 Zxid: 0x100000000 Mode: leader Node count: 5 Proposal sizes last/min/max: -1/-1/-1
若是實踐了上述操做的小夥伴必定會發現,映射路徑下的文件夾多了好多東西,感興趣的小夥伴能夠打開看一下,瞭解下 ZAB 的選舉算法(沒錯,裏邊記錄的就是選舉相關的數據)。
➜ zookeeper cd zoo1 ➜ zoo1 tree . ├── data │ ├── myid │ └── version-2 │ ├── acceptedEpoch │ ├── currentEpoch │ └── snapshot.0 └── datalog └── version-2
注意:留意 currentEpoch 中的數值
5.1 模擬 Leader 掉線
➜ zoo1 docker stop zoo3 zoo3
查看此時的選舉結果(操做同查看角色操做步驟)。能夠看到 Zookeeper 集羣從新選舉結果: zoo2 被選爲 leader
5.2 zoo3 節點從新上線
➜ zoo1 docker start zoo3 zoo3
查看 zoo3 角色,發現 zoo3 自動做爲 follower 加入集羣。
注意:查看 currentEpoch 中的數值,存儲值爲 2,表明通過了 2 次選舉。第一次爲剛啓動時觸發選舉,第二次爲 leader 宕機後從新選舉
筆者本地有安裝的 Zookeeper 環境,因此這裏用本地的 zkCli 進行測試。
zkCli -server localhost:2182,localhost:2183,localhost:2184 Connecting to localhost:2182,localhost:2183,localhost:2184 Welcome to ZooKeeper! JLine support is enabled WATCHER:: WatchedEvent state:SyncConnected type:None path:null [zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 0] ls / [zookeeper]
順序節點保證 znode 路徑將是惟一的。
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 1] create -s /zk-test 123 Created /zk-test0000000000 [zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 2] ls / [zk-test0000000000, zookeeper]
當會話過時或客戶端斷開鏈接時,臨時節點將被自動刪除
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 3] create -e /zk-temp 123 Created /zk-temp [zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 4] ls / [zk-test0000000000, zookeeper, zk-temp]
臨時節點在客戶端會話結束後就會自動刪除,下面使用 quit 命令行退出客戶端,再次鏈接後便可驗證。
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 5] quit Quitting... ➜ docker zkCli -server localhost:2182,localhost:2183,localhost:2184 Connecting to localhost:2182,localhost:2183,localhost:2184 Welcome to ZooKeeper! JLine support is enabled WATCHER:: WatchedEvent state:SyncConnected type:None path:null [zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 0] ls / [zk-test0000000000, zookeeper]
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 1] create /zk-permanent 123 Created /zk-permanent [zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 2] ls / [zk-permanent, zk-test0000000000, zookeeper]
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 3] get / cZxid = 0x0 ctime = Thu Jan 01 08:00:00 CST 1970 mZxid = 0x0 mtime = Thu Jan 01 08:00:00 CST 1970 pZxid = 0x400000008 cversion = 3 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 0 numChildren = 3 [zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 4] ls2 / [zk-permanent, zk-test0000000000, zookeeper] cZxid = 0x0 ctime = Thu Jan 01 08:00:00 CST 1970 mZxid = 0x0 mtime = Thu Jan 01 08:00:00 CST 1970 pZxid = 0x400000008 cversion = 3 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 0 numChildren = 3
使用 ls2 命令來查看某個目錄包含的全部文件,與 ls 不一樣的是它查看到 time、version 等信息
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 5] set /zk-permanent 456 cZxid = 0x400000008 ctime = Tue Mar 03 21:35:20 CST 2020 mZxid = 0x400000009 mtime = Tue Mar 03 21:40:11 CST 2020 pZxid = 0x400000008 cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 3 numChildren = 0
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 6] stat /zk-permanent cZxid = 0x400000008 ctime = Tue Mar 03 21:35:20 CST 2020 mZxid = 0x400000009 mtime = Tue Mar 03 21:40:11 CST 2020 pZxid = 0x400000008 cversion = 0 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 3 numChildren = 0
[zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 7] rmr /zk-permanent [zk: localhost:2182,localhost:2183,localhost:2184(CONNECTED) 8] ls / [zk-test0000000000, zookeeper]
公衆號【當我趕上你】, 天天帶給你不同的內容