博客 / 詳情

返回

百萬架構師第二十九課:協調服務-zookeeper:初步認識zookeeper|JavaGuide

原文鏈接
  1. 從架構的發展過程説起
  2. 什麼是zookeeper
  3. Zookeeper安裝部署
  4. Zoo.cfg配置文件分析
zookeeper 下邊有 zookeeper.out 日誌記錄

架構不是一蹴而就的,是隨着我們業務量的不斷增加,不斷去演變的。

架構的演變

單體架構 tomcat war

JavaGuide-Zookeeper-初步認識-單體-Tomcat-War.png

目標是很快地解決產品的迭代問題,交付問題。

單體架構

體量增長,後端的架構的性能有瓶頸,後端的架構無法支撐如此大的流量。

垂直的方式,增加硬件設備的個數、性能,提升整體的性能。

投入的產出比會越來越小。

然後採用水平的伸縮方式提升架構的性能。

在不改變應用程序和應用程序狀態的情況下,通過水平的伸縮去提升整體的性能。

水平伸縮

對完整的產品進行一個拆分,拆分成不同的模塊。

一般來説,對於一個產品一般是根據業務領域進行拆分,

例子:用户服務, 訂單服務 商品服務

JavaGuide-Zookeeper-初步認識-Webservice-訂單服務.png

遠程過程調用

例如 :基於SOAP協議的webservice進行遠程過程調用,

形成分佈式架構

分佈架構通過不同的計算機網絡通過遠程過程調用組成的一個整體。

當服務越來越多,通過拆分服務,服務會拆分越來越細,做水平擴展,做集羣高可用。

服務成百上千個,某個服務可能做成成百上千的集羣。

JavaGuide-Zookeeper-初步認識-Webservice-訂單服務多台實例.png

HTTP、SOAP、WSDL

在每個節點上需要去維護遠程服務接口的信息。IP地址、參數格式。

調用其他服務時需要根據wsdl文檔生成對應的一些代碼去做遠程過程調用。

拆分服務

如果某一個服務不是一個單一的節點,他進行了成百上千的高可用的配置大規模的集羣。

那麼就必須得維護成百上千的訪問地址的維護。

JavaGuide-Zookeeper-初步認識-Webservice-訂單服務多台實例-地址管理.png

怎麼解決地址維護的管理

如何轉發,負載均衡

而且調用這個服務時請求如何進行轉發。負載均衡機制。

如果集羣中的某個節點宕機了怎麼辦,會有很多錯誤的請求

產生了三個問題:

  1. 協議地址的維護
  2. 負載均衡機制
  3. 服務動態的上下線感知

JavaGuide-Zookeeper-初步認識-Webservice-訂單服務多台實例-地址管理-協議地址維護.png

Zookeeper由此產生了。

需要有一箇中間件,

當服務發佈的時候,告訴中間件,(類似於電話簿的功能)這個服務的地址。

他需要記住所有服務的地址,並且服務斷開的話,還需要知道服務斷開了

消費端在需要調用服務端的時候直接去中間件去獲得地址。在客户端就不需要一個一個地去維護服務端的地址。在客户端,我只需要維護中間件的地址就好了。從中間件中獲得目標服務的信息。拿到目標服務的地址以後就可以做一個轉發,List地址簿,根據對應的負載算法,完成一個轉發。而且中間件也可以感知服務發佈以後的宕機與上線情況。並且客户端,從中間件上拿到地址以後還可以感知到服務端的宕機與上線的變化。

這是我們的目標。

JavaGuide-Zookeeper-初步認識-註冊中心地址薄.png

Zookeeper

Zookeeper是一種樹形結構

Zookeeper數據的數據方式是基於key-value形式,

但它的存儲方式是基於樹形的文件方式的存儲

JavaGuide-Zookeeper-初步認識-zookeeper-目錄結構.png

所以每一種服務建立一個節點,在這個節點下再建立多個節點維護地址信息。客户端只需要檢測這個節點就可以拿到所有的地址信息,然後進行處理和調用。

有人説zookeeper不適合做註冊中心

但是很多公司都用它在做註冊中心,問題不是很大。

Zookeeper是一個分佈式協調服務

它是一個“動物園”,用來管理我們的中間件組件。

Zookeeper歷史:

Zookeeper起源於雅虎,一開始是為了解決分佈式鎖的問題。

如果多個服務調用同一塊資源,然後就會存在資源競爭的問題。

JavaGuide-Zookeeper-初步認識-zookeeper-中間件本身的地址問題-zookeeper歷史.png

多線程是一個進程裏邊的多個線程的併發的問題。可以通過(synochronized lock)去做。

而在分佈式架構中是多進程,傳統的鎖是沒辦法解決的。

所以我們通過zookeeper去協調在各個節點之間去控制節點的接入順序,所以叫zookeeper協調。

這是zookeeper最初的設計目標。

JavaGuide-Zookeeper-初步認識-zookeeper-中間件本身的地址問題-zookeeper設計目標.png

如果在協調的時候成百上千個服務訪問同一塊資源,這時候就會存在中間件的單點問題。所以我們會對zookeeper做集羣。

我們對中間件做集羣是為了保證中間件的高性能,高可用。

然後就會存在中間件集羣上的數據同步問題。

JavaGuide-Zookeeper-初步認識-zookeeper-數據同步問題.png

我們zookeeper集羣中有一個leader兩個slave,我們的每個節點上都要存儲數據,

我們

就是我們每一個zookeeper節點上的數據的同步的問題。

CAP理論,既要保證高性能,還要保證高可用是做不到的!!!存在網絡通訊的狀態。

Zookeeper集羣的數據的提交方式是基於2PC的二階提交的方式去做的。

它的一致性是通過最終一致性來實現的。

Zookeeper可以有多個同級節點。

JavaGuide-Zookeeper-初步認識-zookeeper-服務實例目錄.png

Zookeeper有了很多個服務節點,一個zookeeper上的資源(相當於單點),不同的服務不可以同時地訪問這個zookeeper上的資源,這時候就會存在瓶頸。

Zookeeper集羣就產生了。

Zookeeper集羣是一主多從的機制,

一個leader

Follower follower

做這個集羣能夠保證流量可以被分攤,同時也可以保證高可用,如果leader壞了,可以進行新的選舉,選舉出一個新的leader替換上。這是它用來完成高可用的一種機制。

我們現在的zookeeper是一個集羣,我們的客户端可以訪問集羣中的任意一個節點。

這時候就需要數據的同步。

如果我們用户服務在leader上創建了一個服務,然後訂單服務在follower上去查詢的時候發現這個服務不存在!! 這時候就存在數據不一致的情況。因此我們集羣就需要涉及數據的同步。

我們一定要有leader

我們所有的事務請求要放在leader上,我們的其他請求要放在follower上。

如果我們的事務請求可以放在任意一個節點上,這時候就意味着沒有中心化節點。

Redis-cluster就是一個無中心化的集羣(基於GossIP的協議)

無中心化的時候,那麼事務的控制就會變得更加地複雜。

為了簡化複雜性,

所以我們一般採取

      Leader

Follower     follower

集羣的機制。

所有的事務請求都落在leader節點上,然後其他請求落在follower節點上

我們的事務請求落在leader上時,需要同步數據到follower上,

這時候數據的同步就涉及了CAP,

這時候既要滿足我們可以對外提供服務,也要保證這是高性能的。

如果我們的用户服務往leader上創建了一個節點,但是同步完成之前,我不能給用户一個響應的話,那麼這時候用户服務去操作數據的時候就會一直等待。如果操作需要等待,性能就會降低,所以我們不可能用強一致性。

Zookeeper基於2PC協議完成了一個改造版的2PC協議去保證最終一致性。

去中心化

現在我們的項目都是有一箇中心化,有一個項目組長,一幫碼農去開發,然後他給你分配任務。

這個時候這個項目組長就是一箇中心節點。

如果項目組長身體不好,就會重新選出一個組長來開發,

所謂的去中心化,就是一個團隊裏邊,沒有所謂的組長,所有人都是一個平等的,全都是程序員。然後按照領域去劃分任務,一個任務過來了,判斷屬於哪一個領域,然後誰去處理就好了。去中心化的好處就是不會出現單點故障。

全球互聯網化就是一個去中心化的思想。就是一個機房中的一個電腦出現故障不會影響整個互聯網內其他電腦的是否可以上網,這就是一個去中心化的好處。

通信異常

通過分佈式架構演變以後,我們的每一個服務調用,它是基於一個遠程服務調用。遠程服務通信它本身就是不可靠的,由於一些不可控的因素導致,這個服務在某個時間內是不可用的。如果服務的通信不可用的話,就會存在訪問的消息的丟失。

存在三態

正常情況下只會存在兩種狀態

  • 一種是成功
  • 一種是失敗

然後在分佈式機構中會存在第三種狀態,

  • 未知狀態,可能是超時

可能我們的請求發過去以後,可能已經收到了,處理了,可能一直沒有返回,也有可能在返回過程中出現網絡故障,就會出現超時。

Zookeeper

Zookeeper 它是一個分佈式協調服務,它是由雅虎創建的,是google chubby開源實現的,它是為了解決分佈式架構裏的複雜的容易出錯的一致性的問題,就是我們的協調問題。這是zookeeper設計的初衷。

凡是一些中間件都會有一個配置文件

Zookeeper是開源的。直接在官網上去下載,會有一個 *.tar.gz 的壓縮包

#  tar –zxvf zookeeper-3.4.10.tar.gz

解壓縮以後就可以運行了

解壓縮以後可以在zookeeper的目錄下

#  cd zookeeper-3.4.10
# ls

啓動之前還需要改一下配置文件

# cd conf/
# ls
# cp zoo_sample.cfg zoo.cfg

默認會讀取 zoo.cfg這個文件,我們拷貝這個文件。

Zookeeper有個配置文件,凡是中間件都有一個對應的配置文件,去對一些參數做一些優化和去配置一些可自定義的一些參數

# vim zoo.cfg

可以看到一些配置文件

# cd ..
# cd bin/

進入到bin目錄下

# sh zkServer.sh start
然後zookeeper啓動以後
# sh zkCli.sh
連接

這樣我們就連接到了遠程服務,我們通過客户端連接到了遠程服務,然後我們可以進行一些操作。

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 0]

我們一個客户端通過zkClient.sh去訪問zookeeper,然後就可以遠程操作命令,

Zookeeper是一個樹形的操作結構,它只是一個樹形結構,但是數據都會存在節點上

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

數據怎麼去創建,涉及創建命令

客户端要發起一些命令去操作這些節點

create
delete
set
get

創建的時候必須一層一層創建。

刪除的時候必須一層一層地往上刪除。

# Help

命令可以看到

# Create /orderservice 0

# Ls /

# Create   path    value

作為一個設計者,一定要具備節點的唯一性,在當前的節點下,不能出現名字一樣的兩個節點

Zookeeper的特性:

  1. 同級節點的唯一性
  2. 臨時節點和持久化節點的特性
  3. 有序節點和無序節點
  4. 節點存在父子關係,必須有先後
  5. 臨時節點下不能存在子節點

臨時節點的特性:

在客户端建立的會話週期中,創建的臨時節點,會話結束以後會自動刪除。

有序節點:

如果我們創建的是有序節點的話,會帶有一個數字 00000000000001

00000000000002

創建一個節點

[zk: localhost:2181(CONNECTED) 31] create /orderservice 1
Created /orderservice
[zk: localhost:2181(CONNECTED) 32] create /orderservice/wsdl darian
Created /orderservice/wsdl
[zk: localhost:2181(CONNECTED) 33] get /orderservice/wsdl
darian
cZxid = 0xd
ctime = Wed Jul 04 18:49:26 CST 2018
mZxid = 0xd
mtime = Wed Jul 04 18:49:26 CST 2018
pZxid = 0xd
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
Create –s –e path data acl 

-s  sequence     有序
-e  ephemeral   臨時特性

我們可以基於這兩個參數去創建我們的節點。

Create –s /seq 1 

自動地都會有一個序號

有序節點

[zk: localhost:2181(CONNECTED) 40] create -s /seq/ 1
Created /seq/0000000000
[zk: localhost:2181(CONNECTED) 41] create -s /seq/ 1
Created /seq/0000000001
[zk: localhost:2181(CONNECTED) 42] create -s /seq/ 1
Created /seq/0000000002
[zk: localhost:2181(CONNECTED) 43] create -s /seq/ 1
Created /seq/0000000003
[zk: localhost:2181(CONNECTED) 44] create -s /seq/ 1
Created /seq/0000000004
[zk: localhost:2181(CONNECTED) 45] create -s /seq/ 1
Created /seq/0000000005
[zk: localhost:2181(CONNECTED) 46]

查看節點

[zk: localhost:2181(CONNECTED) 46] ls /seq
[0000000001, 0000000000, 0000000003, 0000000002, 0000000005, 0000000004]

Ctrl + c 斷開連接

再次連接上,臨時節點就被刪除了

Key-value形式可以存儲序列化的對象。

節點必須有父節點,才能創建子節點。

Zoo_sample.cfg 裏邊

dataDir=tmp/zookeeper :存儲數據的文件的路徑

zookeeper默認讀取 zoo.cfg 文件

zkCli.sh zookeeper自帶的客户端腳本

臨時節點下不能有子節點

[zk: localhost:2181(CONNECTED) 2] create -e /tmp 1
Created /tmp
[zk: localhost:2181(CONNECTED) 3] create /tmp/aaa 3
Ephemerals cannot have children: /tmp/aaa
[zk: localhost:2181(CONNECTED) 4] create -e /tmp/aaa 3
Ephemerals cannot have children: /tmp/aaa
[zk: localhost:2181(CONNECTED) 5]

配置zookeeper集羣

可能需要安裝vim 插件

# yum install -y vim 
# vim zoo.cfg

下邊加上

​ 前邊是集羣之間通信的接口,後邊是用來選舉的端口

## server.id=ip:port:port


server.1=192.168.136.128:2888:3888
server.2=192.168.136.130:2888:3888
server.3=192.168.136.131:2888:3888

(啓動一次zookeeper才有對應的目錄)

# vim /tmp/zookeeper/myid

在data目錄下創建myid(id一定要和上面id對應的ip對應)

i

把server的id放進去

查看進程

# ps –ef|grep zookeeper
# Kill -9 40290

直接關掉進程

# sh zkServer.sh stop

看一下日誌:

# tail –f zookeeper.out


2018-07-05 00:52:30,493 [myid:1] - INFO [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:QuorumPeer$QuorumServer@184] - Resolved hostname: 192.168.168.131 to address: /192.168.168.131
2018-07-05 00:52:30,493 [myid:1] - INFO [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:FastLeaderElection@847] - Notification time out: 60000
2018-07-05 00:53:30,510 [myid:1] - WARN [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:QuorumCnxManager@584] - Cannot open channel
to 2 at election address /192.168.136.130:3888

這個時候會報錯

Cannot open channel to 3 at election address /192.168.111.155:3888

這個端口是用來做leader選舉的

報錯

Java.net.NoRouteToHoHostExceptoion:No route to host

防火牆沒有關

# systemctl stop firewalld

關閉防火牆

成功啓動了

2018-07-05 01:05:18,440 [myid:1] - INFO [LearnerHandler-/192.168.136.131:33468:LearnerHandler@346] - Follower sid: 3 : info : org.apache.zookeeper.server.quorum.QuorumPeer$QuorumServer@30dac10b
2018-07-05 01:05:18,448 [myid:1] - INFO [LearnerHandler-/192.168.136.131:33468:LearnerHandler@401] - Synchronizing with Follower sid: 3 maxCommittedLog=0x1b minCommittedLog=0x1 peerLastZxid=0x0
2018-07-05 01:05:18,448 [myid:1] - WARN [LearnerHandler-/192.168.136.131:33468:LearnerHandler@468] - Unhandled proposal scenario
2018-07-05 01:05:18,448 [myid:1] - INFO [LearnerHandler-/192.168.136.131:33468:LearnerHandler@475] - Sending SNAP
2018-07-05 01:05:18,449 [myid:1] - INFO [LearnerHandler-/192.168.136.131:33468:LearnerHandler@499] - Sending snapshot last zxid of peer is 0x0 zxid of leader is 0x100000000sent zxid of db as 0x1b
2018-07-05 01:05:18,457 [myid:1] - INFO [LearnerHandler-/192.168.136.131:33468:LearnerHandler@535] - Received NEWLEADER-ACK message from 3
2018-07-05 01:05:18,457 [myid:1] - INFO [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:Leader@962] - Have quorum of supporters, sids: [ 1,3 ]; starting up and setting last processed zxid: 0x100000000
2018-07-05 01:05:22,252 [myid:1] - INFO [LearnerHandler-/192.168.136.130:50564:LearnerHandler@346] - Follower sid: 2 : info : org.apache.zookeeper.server.quorum.QuorumPeer$QuorumServer@7d15c17f
2018-07-05 01:05:22,261 [myid:1] - INFO [LearnerHandler-/192.168.136.130:50564:LearnerHandler@401] - Synchronizing with Follower sid: 2 maxCommittedLog=0x1b minCommittedLog=0x1 peerLastZxid=0x0
2018-07-05 01:05:22,261 [myid:1] - WARN [LearnerHandler-/192.168.136.130:50564:LearnerHandler@468] - Unhandled proposal scenario
2018-07-05 01:05:22,261 [myid:1] - INFO [LearnerHandler-/192.168.136.130:50564:LearnerHandler@475] - Sending SNAP
2018-07-05 01:05:22,262 [myid:1] - INFO [LearnerHandler-
/192.168.136.130:50564:LearnerHandler@499] - Sending snapshot last zxid of peer is 0x0 zxid of leader is 0x100000000sent zxid of db as 0x100000000
2018-07-05 01:05:22,270 [myid:1] - INFO [LearnerHandler-/192.168.136.130:50564:LearnerHandler@535] - Received NEWLEADER-ACK message from 2

查看節點的狀態:

# sh zkServer.sh status
[root@Darian1 bin]# sh zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /zookeeper/zookeeper-3.4.12/bin/../conf/zoo.cfg
Mode: leader
Zookeeper JMX enabled by default
Using config: /data/program/zookeeper-…….
Mode: follower

到follower中可以看到數據的同步現象

# sh zkCli.sh
# ls / 

在 data 目錄下創建myid(id要和上面的id對應的ip對應)

Observer

在集羣中還有一個Observer的角色

Observer

起到一個監控的作用

在不影響整個集羣性能的情況下,還能夠知道整個集羣的狀態

回顧:

JavaGuide-Zookeeper-初步認識-zookeeper-服務地址-臨時節點.png

我們希望有一箇中間件可以實現我們的服務發現,地址感知,和負載均衡。

所以有了zookeeper

我們需要一個註冊中心去管理我們的地址,我們的客户端都會調用zookeeper上的地址。

被調用方在zookeeper上創建節點

訂單服務在zookeeper上創建了一個order節點,(持久化節點)

然後我們在order節點下可以創建很多個節點,每個節點上存儲的是對應的協議地址,可以創建多個。如果此刻這個服務的集羣有一千個,那麼我需要在這個節點下維護一千個地址。

我們的協議地址,就需要用臨時節點。

我們前邊説要做到服務動態上下線感知。就意味着,服務宕機以後在這個節點上要有體現。這個節點會自動刪除。

臨時節點的好處:它的生命週期和當前的會話是保持一致的。

我們啓動以後,通過我們的封裝,就是底層的封裝,dubbo就是這樣封裝的。

調用方,連接zookeeper拿到這個信息,

叫做訂閲:

訂閲:叫做watcher機制

就是對應節點如果發生變化了,會有一個事件通知。

節點一旦發生地址的變化,就會有通知。我們就可以拿到相應的數據去做處理。去判斷當前節點的狀態,或者我們去更新當前節點的信息。就能夠拿到我們最新的地址,以及我們拿到地址以後就可以做負載均衡。

先了解zookeeper的核心:

# cd conf/

任何中間件裏邊都有一個配置文件,redis有一個叫redis.cfg

Zookeeper有一個叫 zoo.cfg

Nginx有一個配置文件叫 nginx.cfg

因為中間件需要提供一些需要優化的配置,我們可以去調整我們的線程、緩衝區,去配置我們的相關地址。

詳細配置去官網上看,可以根據需要去做很多的調整

TickTime-2000

它可以理解成一個時間單位,是我們zookeeper裏邊最小的時間單位毫秒。
每2毫秒:相當於我們定義 了一個時間單位的規則。
initLImit=10

初始化,就是初始化同步數據的時候  ,10表示10個tickTime

最大的同步時間、初始化時間。

syncLimit=5

發起一個請求,和獲得請求的狀態的一個時間,就是數據同步的時間

這是leader和follower之間的心跳檢測最大延遲,是5個tickTime ,就是10秒鐘。

如果集羣中我們的follower節點不可用,或者我們的leader節點不可用,這時候這樣的一個心跳檢測就會出現一個故障,如果心跳出現一個故障,就意味着整個集羣中出現某個節點不可用,就會重新去選舉

dataDir=/tmp/zookeeper

這是大家關注的。

我們對應的事務的日誌文件會存在dataDir文件裏邊,

[root@Darian1 conf]# ls /tmp/zookeeper/version-2/
acceptedEpoch currentEpoch log.1 log.100000001 log.200000001

Tmp是一個臨時節點,所以我們不建議放在這個目錄下,我們建議獨立掛載在一個SSD的高速磁盤上,因為磁盤的讀取速度決定了我們zookeeper集羣的一個性能,因為我們的每一個事務請求,就是我們的每一個insert,update 和 delete,是屬於事務請求,那麼每一個事務請求,它都會先去記錄事務日誌,所以記錄日誌這個盤寫入比較慢,那麼這個整體的性能就會受到很大的影響。

clientPort=2181

客户端和服務端連接的時候用到的一個端口

redis:6379  tomcat 8080

這是一個對外提供的一個開放的端口

Server.1=192.168.11.153:2888:3888

後邊對應的是myid myid是默認找到dataDir裏邊的一個myid文件,去讀取這個id。

這個id後邊會用

這個myid對應的ip地址,通信端口號,選舉端口

# ls /tmp/zookeeper/version-2

可以看到

AcceptedEpoch
currentEpoh
log.1
log.3

策略

打開我們的zookeeper客户端

# sh zkCli.sh 

# ls /

可以看到節點

我們用到了很多命令,並做了一些簡單的策略,比如説臨時節點策略和持久節點策略

(m 修改 modifier 修改)

#> get /zzz

去獲取一個節點的話可以獲得很多信息

[zk: localhost:2181(CONNECTED) 4] get /seq
seq
cZxid = 0xf
ctime = Wed Jul 04 19:03:16 CST 2018
mZxid = 0xf
mtime = Wed Jul 04 19:03:16 CST 2018
pZxid = 0x15
cversion = 6
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 6

value值

cZxiid= 0x1000000000003    
表示事務id,我們去創建一個節點的時候相當於發起一個事務請求,他會有個事務id
ctime=Sun Jun 03 21:08:21 CST 2019
創建時間
mZxid=0x100000000000003
我們對節點進行更改的時候,進行更新事務,就會有更新事務id,如果我們在創建了節點以後他的創建id和更新id是一樣的。
mtime= Sun Jun 03 21:08:21 CST 2019
修改時間
pZxid=0x1000000000003

這個表示當前節點下,最後一次被修改的時候,他的一個事務id,會放到pZxid裏邊。
只有子節點變更以後才會產生pZxid的影響
cversion=0
當前節點的子節點版本號
dataVersion=0
當前數據內容它的版本號
aclVersion=0
當前權限變更的版本號

這時候有一個version,它是一個樂觀鎖的概念,我們會通過一個版本號去維護數據的版本。如果説我們a、b同時去修改一個數據,這個數據修改以後它的版本發生了變化,a先,b後的話,b基於以前的數據進行修改的話,得到的數據不對,這就是我們樂觀鎖的概念。通過版本號去維護數據的狀態。

這些版本號用來控制數據的併發性,因為zookeeper一定存在併發訪問。記錄版本號,是為了防止對髒數據的一個修改導致最終數據不一致,所以我們在修改的時候需要一個版本號。

(我們在更新節點的時候傳入一個版本號 set /zzz value 0 ,這時候 dataaVersion=1,我們在 set /zzz value 0,這個時候它會報錯,version No is not valid : /mic 這就是我們用版本號去控制被錯誤修改的一個重要的手段)

[zk: localhost:2181(CONNECTED) 7] set /seq aa 0
    cZxid = 0xf
    ctime = Wed Jul 04 19:03:16 CST 2018
        mZxid = 0x200000003
        mtime = Thu Jul 05 15:54:53 CST 2018
            pZxid = 0x15
            cversion = 6
            dataVersion = 1
            aclVersion = 0
            ephemeralOwner = 0x0
            dataLength = 2
            numChildren = 6
            [zk: localhost:2181(CONNECTED) 8] set /seq aa 0
            version No is not valid : /seq
    [zk: localhost:2181(CONNECTED) 9] set /seq bb 1
                cZxid = 0xf
                ctime = Wed Jul 04 19:03:16 CST 2018
                    mZxid = 0x200000005
                    mtime = Thu Jul 05 15:56:21 CST 2018
                        pZxid = 0x15
                        cversion = 6
                        dataVersion = 2
                        aclVersion = 0
                        ephemeralOwner = 0x0
                        dataLength = 2
                        numChildren = 6
                        [zk: localhost:2181(CONNECTED) 10]
ephemeralOwner=0X0

創建臨時節點以後才會有的, 我們創建一個臨時節點之後,get /temp之後

這個客户端與zookeeper會有一個會話的標誌,通過這個標誌,我們可以知道這個連接斷開以後我們應該把那些臨時節點都刪掉,這個就是用來綁定當前會話的信息

dataLength=3
當前數據的長度  
numChildren=0
當前子節點的一個數量 
每一個滴答聲的毫秒數
tickTime = 2000
最初的蜱蟲數量
同步階段可以
initLimit = 10
可以在兩者之間傳遞的節拍數
發送請求並獲得確認
syncLimit = 5
存儲快照的目錄。
不要使用/tmp存儲,/tmp只是
#例子的緣故。
dataDir = / tmp /動物園管理員
客户端連接的端口
clientPort = 2181
客户端連接的最大數量。
如果你需要處理更多的客户,就增加這個
# maxClientCnxns = 60
#
請務必閲讀以下內容的維護部分
管理員指南,在開啓自動提醒之前。
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html sc_maintenance
#
在dataDir中保存的快照數量
# autopurge.snapRetainCount = 3
在小時內清除任務間隔
設置為“0”以禁用自動清除功能
# autopurge.purgeInterval = 1

ACL

ACL是一個權限控制

Zookeeper提供了一種類似於linux的文件控制權限的一種方式。去控制你創建的文件夾的訪問權限。比如説我們創建了某一個節點,只能讀,或只能讓某一個用户訪問怎麼辦。就是通過ACL去控制。

ACL定義了五種權限。

CREATE/READ/WRITE/DELETE/ADMIN

每一權限控制的粒度不一樣,控制的方向不一樣,這就是我們節點的一個特性。

名詞覆盤:

集羣角色

我們集羣中有leader follower,各個集羣之間是可以通信的。

JavaGuide-Zookeeper-初步認識-zookeeper-Leader-Follower集羣.png

                 Leader

Follower           follower             observer

Leader是用來處理事務請求的,所有的添加,修改,刪除,可以理解為一個讀寫分離的概念。

如果我們客户端將事務請求發給了follower,那麼這個follower會把請求轉發給leader

數據庫請求默認都會打開一個事務。Observer是一個監視,Observer只負責去leader節點同步數據,不會參與這個節點的選舉,不涉及選舉,就不會有性能的開銷。它的數據的同步就不需要做到一個實時。Observer節點是可以去配置的。

數據模型:

Zookeeper核心的東西

我們可以創建多個節點,

每個節點下我們又可以創建多個節點。

節點:

  • 持久化節點
  • 持久化有序節點
  • 臨時節點
  • 臨時有序節點
  • 統計節點的唯一性

JavaGuide-Zookeeper-初步認識-zookeeper-節點特性.png

每一個節點叫做znode,表示他的最小的一個數據單位的名稱叫做znode。

Znode上可以保存少量的數據,不建議保存太多。因為如果你一個節點的數據量很大,那麼同步的性能就會很低,第二個,你如果要獲得這樣一個節點信息,需要通過網絡的傳輸。那麼如果數據包比較大,那它就會比較慢。

會話:

會話是個概念,就是客户端與服務端進行一次連接時,就會產生一次會話。

就像我們創建一個數據庫的連接,建立一個connection,創建一個redis的連接,會建立一個redis-connection,這就是一個連接。這樣的連接底層就是一個TCP連接。

那麼連接的狀態有幾種,它是變化的。

JavaGuide-Zookeeper-初步認識-zookeeper-會話狀態.png

首先是沒有連接的狀態 NOT CONNECTED 默認狀態
然後發起連接 CONNECTING 客户端去初始化連接的時候
然後客户端連接成功以後 CONNECTED 就是連接成功的一個狀態
最後處理完成以後 CLOSE

這是一個會話的生命週期

Connected也有可能變成connecting,客户端丟失連接的時候,然後他就會去重試連接。然後他重試連接的時間是可以在zoo.cfg中去配置的。如果一直連接不上,就會變成close的狀態。主動關閉這個連接,會話失效。

這是zookeeper裏邊會話狀態的一個改變。

Status

節點的屬性,前邊有!!!

Watcher機制

JavaGuide-Zookeeper-初步認識-zookeeper-Watcher機制.png

我們客户端可以去監聽某一個節點,可以去watcher某一個節點。

客户端watcher這個節點以後,意味着接下來這個節點下的數據的變更,它會有個事件通知。類似於分佈式系統的發佈訂閲,這就是一個事件的通知。我們的客户端就可以被動地知道服務器端的協議信息的狀態以及我們節點變更以後更新的一個操作。

ACL權限控制

上邊有!

Zookeeper的應用場景:

Zookeeper可以實現註冊中心,通過註冊中心拿到一些對應的信息。這是zookeeper第一個特性。

配置中心

JavaGuide-Zookeeper-初步認識-zookeeper-作為配置中心.png

我們還可以實現配置中心。他們實現起來有點大同小異,但在實際使用過程中是不一樣的。

我們開發程序時都會存在一個總的配置文件:比如説 application.contextapplication.properties 這樣一個文件。

這樣的文件會維護在每一個對應的項目裏邊,比如説我們存在一些數據庫的配置,一些常量的配置。我們都放在配置文件裏邊,後續修改起來很麻煩,第二個維護起來工作量大。

第三個在於我們的開關,怎麼去做。比如説我們要去控制流程的走向。我們通過一個開關的方式去控制,那如果我們現在要將整個工程變成另外一個方向去走的話。

比如説我們if 走 A if 走B else 走C,

然後我們現在需要流程變化了,我們需要去重新更改配置文件,去發佈,讀取配置文件,或者也可以去動態監控這個配置文件。實現起來很麻煩。

如果這個開關需要同步給多個節點,我們就需要在每個節點去實現這樣的一個功能。所以我們就需要一個配置中心來統一的維護,可以在上邊託管的一些配置。客户端要訪問,我要去配置中心去拿到這個值。你在配置中心配置一個switch,配置中心告訴你的value是這個switch=on。我拿到這個值就可以去判斷,這樣子就是一個配置中心的一個概念。

那麼zookeeper實現配置中心有兩個好處

第一個,我們可以去監聽某個節點的變化,可以去watcher,我就可以不需要主動地去拿,然後動態地去感知,動態地push過來。 這是第一個。

第二個,zookeeper它裏邊本身的一個節點的特性,就是我們可以通過節點去管理我們相應的一些相關的數據,還有它本身提供了安全機制。

第三個,我們可以去對節點做一個安全方式的處理,

所以這是zookeeper能夠實現配置中心的好處,還有zookeeper的性能也很高。

Zookeeper 可以實現負載均衡

JavaGuide-Zookeeper-初步認識-zookeeper-負載均衡.png

負載均衡有兩點:

  1. 第一個我要知道機器的狀態
  2. 選舉master

我們選舉master和zookeeper的leader的選舉有什麼區別?

我們可以去基於zookeeper去實現leader選舉,比如説kafka是基於zookeeper去實現的,比如説我現在要建立一個kafka集羣,有三個節點,怎麼實現集羣?當然kafka集羣不是內部去實現的,zookeeper內部是通過leader選舉通過ZAB協議進行選舉的,這是它內部提供的機制,但其他的中間件沒有。

Kafka是基於zookeeper,在zookeeper上去註冊節點

Kafka01    kafka02      kafka03

那我們每一個kafka啓動的時候都往zookeeper上去註冊一個節點,同一個節點下,節點最小就是leader

這樣子我們就用zookeeper的機制去完成對kafka集羣的選舉,

如果對應kafka集羣的leader節點掛掉了,這時候zookeeper有一個事件,事件可以通知給其他的節點。當前這個leader節點掛掉了(watcher)。拿到這個通知以後,又要重新地進行選舉,然後再拿到最小的節點 kafka2 拿到,把它定義成為master。

這是zookeeper實現負載均衡的概念

是基於zookeeper的提醒去實現其他節點的一個功能與管理

所以説zookeeper是一個分佈式協調服務,

所以它能夠協調很多服務的特性

Zookeeper還可以實現分佈式鎖

Watcher機制的演示

連接上zookeeper客户端以後

# Get /zzz   true

表示這個節點變化以後會有事件通知

然後連上另一個客户端,

# Set /zzz    true

在get的那個客户端中就會看到一個提醒:

WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/mic

我們在zookeeper上通過它的節點的特性,是一個文件結構的方式,創建節點會有一個結點的順序,那我們可以通過客户端去訪問節點的時候,去創建節點,最小的節點就是leader,基於zookeeper去實現選舉

樂觀鎖:

每一個數據都存在一個版本號。

> Get /mic

可以得到一個版本號 當前是一個3

然後另一個設置一下

>   set /mic 3

版本號為4

但是我們

>   set /mic  bbb  3  

這時候我們傳遞之前的一個版本號,

他會報錯

       version No is not valid : /mic

數據版本號的變化是通過樂觀鎖去實現的

Zookeeper的選舉有三種方式

創建節點的時候會直接存在磁盤裏,有事務日誌

前邊的端口是用來做數據同步的,

後邊的端口是用來做leader選舉時用的,

​ Leader的選舉(當服務初步起來的時候,或者leader掛掉的時候就會進行leader的選舉)

Observer

Observer在不影響zookeeper的寫性能的情況下,zookeeper在leader上執行一個寫操作,同步給其他的節點,訪問量越來越大以後,我們的整個集羣就會很大,我們增加一個節點,會增加一些吞吐量,但是也會帶來數據同步的性能上的問題。Zookeeper集羣整體寫的性能就會下降,所以zookeeper裏邊節點變更數據過來以後,我需要半數的投票以後,才能夠告訴客户端已經存儲了,這個時候就涉及到數據的同步,observer加到裏邊,不會參與到投票,所以observer可以提升性能。

Observer涉及選舉,不參與投票

我們的key-value 的value一般不存什麼內容,就基於節點的特性去實現

日誌上不顯示哪一台是leader,只能通過狀態去顯示哪一個是leader

用kafka必須安裝zookeeper

來源於: https://javaguide.net

公眾號:不止極客

user avatar ticktank 頭像 biubiubiu_5ea3ee0e6b5fd 頭像 saoming_zhang 頭像 tingtr 頭像 async_wait 頭像 u_16213560 頭像 mylxsw 頭像 shadowck 頭像
8 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.