2. zookeeper介紹及集羣搭建

 

ZooKeeper 概述

Zookeeper 是一個分佈式協調服務的開源框架。 主要用來解決分佈式集羣中
應用系統的一致性問題,例如怎樣避免同時操做同一數據形成髒讀的問題。
ZooKeeper 本質上是一個分佈式的小文件存儲系統。 提供基於相似於文件系
統的目錄樹方式的數據存儲,而且能夠對樹中的節點進行有效管理。從而用來維
護和監控你存儲的數據的狀態變化。經過監控這些數據狀態的變化,從而能夠達
到基於數據的集羣管理。 諸如: 統一命名服務(dubbo)、分佈式配置管理(solr的配置集中管理)、分佈式消息隊列(sub/pub)、分佈式鎖、分佈式協調等功能。java

2.一、zookeeper的架構圖

Leader:
Zookeeper 集羣工做的核心
事務請求(寫操做) 的惟一調度和處理者,保證集羣事務處理的順序性;
集羣內部各個服務器的調度者。
對於 create, setData, delete 等有寫操做的請求,則須要統一轉發給leader 處理, leader 須要決定編號、執行操做,這個過程稱爲一個事務。
Follower:
處理客戶端非事務(讀操做) 請求,node

轉發事務請求給 Leader;linux

參與集羣 Leader 選舉投票 2n-1臺能夠作集羣投票。算法

此外,針對訪問量比較大的 zookeeper 集羣, 還可新增觀察者角色。shell

 


Observer:
觀察者角色,觀察 Zookeeper 集羣的最新狀態變化並將這些狀態同步過
來,其對於非事務請求能夠進行獨立處理,對於事務請求,則會轉發給 Leader
服務器進行處理。
不會參與任何形式的投票只提供非事務服務,一般用於在不影響集羣事務
處理能力的前提下提高集羣的非事務處理能力。數據庫

扯淡:說白了就是增長併發的讀請求apache

2.二、zookeeper的特性

1.全局數據一致:每一個 server 保存一份相同的數據副本, client 不管連
接到哪一個 server,展現的數據都是一致的,這是最重要的特徵;
2. 可靠性:若是消息被其中一臺服務器接受,那麼將被全部的服務器接受。
3. 順序性:包括全局有序和偏序兩種:全局有序是指若是在一臺服務器上
消息 a 在消息 b 前發佈,則在全部 Server 上消息 a 都將在消息 b 前被
發佈;偏序是指若是一個消息 b 在消息 a 後被同一個發送者發佈, a 必
將排在 b 前面。
4. 數據更新原子性:一次數據更新要麼成功(半數以上節點成功),要麼失
敗,不存在中間狀態;
5. 實時性: Zookeeper 保證客戶端將在一個時間間隔範圍內得到服務器的
更新信息,或者服務器失效的信息。vim

2.三、三臺機器zookeeper的集羣環境搭建

Zookeeper 集羣搭建指的是 ZooKeeper 分佈式模式安裝。 一般由 2n+1
臺 servers 組成。 這是由於爲了保證 Leader 選舉(基於 Paxos 算法的實
現) 能過獲得多數的支持,因此 ZooKeeper 集羣的數量通常爲奇數。
Zookeeper 運行須要 java 環境, 因此須要提早安裝 jdk。 對於安裝
leader+follower 模式的集羣, 大體過程以下:
l 配置主機名稱到 IP 地址映射配置
l 修改 ZooKeeper 配置文件
l 遠程複製分發安裝文件
l 設置 myid
l 啓動 ZooKeeper 集羣
若是要想使用 Observer 模式,可在對應節點的配置文件添加以下配置:
peerType=observer
其次,必須在配置文件指定哪些節點被指定爲 Observer,如:
server.1:localhost:2181:3181:observer服務器

服務器IPsession

主機名

myid的值

192.168.221.100

node01

1

192.168.221.110

node02

2

192.168.221.120

node03

3

 

第一步:下載zookeeeper的壓縮包,下載網址以下

http://archive.apache.org/dist/zookeeper/

咱們在這個網址下載咱們使用的zk版本爲3.4.9

下載完成以後,上傳到咱們的linux的/export/softwares路徑下準備進行安裝

第二步:解壓

解壓zookeeper的壓縮包到/export/servers路徑下去,而後準備進行安裝

cd /export/softwares

tar -zxvf zookeeper-3.4.9.tar.gz -C ../servers/

第三步:修改配置文件

第一臺機器修改配置文件

cd /export/servers/zookeeper-3.4.9/conf/

cp zoo_sample.cfg zoo.cfg

mkdir -p /export/servers/zookeeper-3.4.9/zkdatas/

vim  zoo.cfg

dataDir=/export/servers/zookeeper-3.4.9/zkdatas

autopurge.snapRetainCount=3

autopurge.purgeInterval=1

server.1=node01:2888:3888

server.2=node02:2888:3888

server.3=node03:2888:3888

 

第四步:添加myid配置

在第一臺機器的

/export/servers/zookeeper-3.4.9/zkdatas/這個路徑下建立一個文件,文件名爲myid ,文件內容爲1

echo 1 > /export/servers/zookeeper-3.4.9/zkdatas/myid

第五步:安裝包分發並修改myid的值

安裝包分發到其餘機器

第一臺機器上面執行如下兩個命令

scp -r  /export/servers/zookeeper-3.4.9/ node02:/export/servers/

scp -r  /export/servers/zookeeper-3.4.9/ node03:/export/servers/

第二臺機器上修改myid的值爲2

echo 2 > /export/servers/zookeeper-3.4.9/zkdatas/myid

三臺機器上修改myid的值爲3

echo 3 > /export/servers/zookeeper-3.4.9/zkdatas/myid

第六步:三臺機器啓動zookeeper服務

三臺機器啓動zookeeper服務

這個命令三臺機器都要執行

/export/servers/zookeeper-3.4.9/bin/zkServer.sh start

查看啓動狀態

/export/servers/zookeeper-3.4.9/bin/zkServer.sh  status

 

2.四、zookeeper的shell操做

2.4.一、客戶端鏈接

運行 zkCli.sh –server ip 進入命令行工具。

輸入 help,輸出 zk shell 提示:

2.4.二、shell操做

建立節點

 

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

 

其中,-s 或-e 分別指定節點特性,順序或臨時節點,若不指定,則表示持 久節點;acl 用來進行權限控制。

 

建立順序節點:

建立永久節點:

讀取節點

 

與讀取相關的命令有 ls  命令和 get  命令,ls 命令能夠列出 Zookeeper 指

 

定節點下的全部子節點,只能查看指定節點下的第一級的全部子節點;get 命令

 

能夠獲取 Zookeeper 指定節點的數據內容和屬性信息。 ls path [watch]

 

get path [watch] ls2 path [watch]

 

set path data [version]

 

data 就是要更新的新內容,version 表示數據版本。

如今 dataVersion 已經變爲 1 了,表示進行了更新。

刪除節點

delete path [version]

若刪除節點存在子節點,那麼沒法刪除該節點,必須先刪除子節點,再刪除父節點。

Rmr path

能夠遞歸刪除節點。

quota

setquota -n|-b val path 對節點增長限制。 n:表示子節點的最大個數 b:表示數據值的最大長度 val:子節點最大個數或數據值的最大長度 path:節點路徑

 

listquota path        列出指定節點的 quota

子節點個數爲 2,數據長度-1 表示沒限制 

delquota [-n|-b] path 刪除 quota

 

其餘命令

history : 列出命令歷史

redo:該命令能夠從新執行指定命令編號的歷史命令,命令編號能夠經過

 

history 查看

 

2.五、zookeeper的數據模型

ZooKeeper 的數據模型,在結構上和標準文件系統的很是類似,擁有一個層

 

次的命名空間,都是採用樹形層次結構,ZooKeeper 樹中的每一個節點被稱爲—

 

Znode。和文件系統的目錄樹同樣,ZooKeeper 樹中的每一個節點能夠擁有子節點。

 

但也有不一樣之處:

 

  1. Znode 兼具文件和目錄兩種特色。既像文件同樣維護着數據、元信息、ACL、 時間戳等數據結構,又像目錄同樣能夠做爲路徑標識的一部分,並能夠具備 子 Znode。用戶對 Znode 具備增、刪、改、查等操做(權限容許的狀況下)。

 

  1. Znode 具備原子性操做,讀操做將獲取與節點相關的全部數據,寫操做也將 替換掉節點的全部數據。另外,每個節點都擁有本身的 ACL(訪問控制列表),這個列表規定了用戶的權限,即限定了特定用戶對目標節點能夠執行的操做。

 

  1. Znode 存儲數據大小有限制。ZooKeeper 雖然能夠關聯一些數據,但並無 被設計爲常規的數據庫或者大數據存儲,相反的是,它用來管理調度數據, 好比分佈式應用中的配置文件信息、狀態信息、聚集位置等等。這些數據的 共同特性就是它們都是很小的數據,一般以 KB 爲大小單位。ZooKeeper 的服 務器和客戶端都被設計爲嚴格檢查並限制每一個 Znode 的數據大小至多 1M,常規使用中應該遠小於此值。

 

  1. Znode 經過路徑引用,如同 Unix 中的文件路徑。路徑必須是絕對的,所以他 們必須由斜槓字符來開頭。除此之外,他們必須是惟一的,也就是說每個 路徑只有一個表示,所以這些路徑不能改變。在 ZooKeeper 中,路徑由 Unicode 字符串組成,而且有一些限制。字符串"/zookeeper"用以保存管理 信息,好比關鍵配額信息。

 

2.5.一、數據結構

圖中的每一個節點稱爲一個 Znode。 每一個 Znode 由 3 部分組成:

 

① stat:此爲狀態信息, 描述該 Znode 的版本, 權限等信息

 

② data:與該 Znode 關聯的數據

 

③ children:該 Znode 下的子節點

2.5.二、節點類型

Znode 有兩種,分別爲臨時節點和永久節點。

 

節點的類型在建立時即被肯定,而且不能改變。

 

臨時節點:該節點的生命週期依賴於建立它們的會話。一旦會話結束,臨時 節點將被自動刪除,固然能夠也能夠手動刪除。臨時節點不容許擁有子節點。

 

永久節點:該節點的生命週期不依賴於會話,而且只有在客戶端顯示執行刪

 

除操做的時候,他們才能被刪除。

 

Znode 還有一個序列化的特性,若是建立的時候指定的話,該 Znode 的名字

 

後面會自動追加一個不斷增長的序列號。序列號對於此節點的父節點來講是惟一

 

的,這樣便會記錄每一個子節點建立的前後順序。它的格式爲「%10d」(10 位數字,

 

沒有數值的數位用 0 補充,例如「0000000001」)。

 

這樣便會存在四種類型的 Znode 節點,分別對應:

 

PERSISTENT:永久節點

 

EPHEMERAL:臨時節點

 

PERSISTENT_SEQUENTIAL:永久節點、序列化

 

EPHEMERAL_SEQUENTIAL:臨時節點、序列化

 

建立永久節點:

[zk: localhost:2181(CONNECTED) 3] create /hello world

Created /hello

建立臨時節點:

[zk: localhost:2181(CONNECTED) 5] create -e /abc 123

Created /abc

建立永久序列化節點:

[zk: localhost:2181(CONNECTED) 6] create -s /zhangsan boy

Created /zhangsan0000000004

建立臨時序列化節點:

zk: localhost:2181(CONNECTED) 11] create -e -s /lisi boy

Created /lisi0000000006

2.5.三、節點屬性

每一個 znode 都包含了一系列的屬性,經過命令 get,能夠得到節點的屬性。

 

dataVersion:數據版本號,每次對節點進行 set 操做,dataVersion 的值都

 

會增長 1(即便設置的是相同的數據),可有效避免了數據更新時出現的前後順

 

序問題。

 

cversion :子節點的版本號。當 znode 的子節點有變化時,cversion 的值

 

就會增長 1。

 

aclVersion :ACL 的版本號。

 

cZxid :Znode 建立的事務 id。

 

mZxid  :Znode 被修改的事務 id,即每次對 znode 的修改都會更新 mZxid。

 

對於 zk 來講,每次的變化都會產生一個惟一的事務 id,zxid(ZooKeeper Transaction Id)。經過 zxid,能夠肯定更新操做的前後順序。例如,若是 zxid1

 

小於 zxid2,說明 zxid1 操做先於 zxid2 發生,zxid 對於整個 zk 都是惟一的,

 

即便操做的是不一樣的 znode。

 

ctime:節點建立時的時間戳.

 

mtime:節點最新一次更新發生時的時間戳.

 

ephemeralOwner:若是該節點爲臨時節點, ephemeralOwner 值表示與該節點

 

綁定的 session id. 若是不是, ephemeralOwner 值爲 0.

 

在 client 和 server 通訊以前,首先須要創建鏈接,該鏈接稱爲 session。連

 

接創建後,若是發生鏈接超時、受權失敗,或者顯式關閉鏈接,鏈接便處於 CLOSED

狀態, 此時 session 結束。

2.六、zookeeper的watch機制

ZooKeeper 提供了分佈式數據發佈/訂閱功能,一個典型的發佈/訂閱模型系

 

統定義了一種一對多的訂閱關係,能讓多個訂閱者同時監聽某一個主題對象,當

 

這個主題對象自身狀態變化時,會通知全部訂閱者,使他們可以作出相應的處理。

 

ZooKeeper 中,引入了 Watcher 機制來實現這種分佈式的通知功能 。

 

ZooKeeper 容許客戶端向服務端註冊一個 Watcher 監聽,當服務端的一些事件觸

 

發了這個 Watcher,那麼就會向指定客戶端發送一個事件通知來實現分佈式的通知功能。

 

觸發事件種類不少,如:節點建立,節點刪除,節點改變,子節點改變等。

 

總的來講能夠歸納 Watcher 爲如下三個過程:客戶端向服務端註冊 Watcher、

 

服務端事件發生觸發 Watcher、客戶端回調 Watcher 獲得觸發事件狀況

2.6.一、watch機制特色

一次性觸發

 

事件發生觸發監聽,一個 watcher event 就會被髮送到設置監聽的客戶端,

 

這種效果是一次性的,後續再次發生一樣的事件,不會再次觸發。

 

事件封裝

 

ZooKeeper 使用 WatchedEvent 對象來封裝服務端事件並傳遞。

 

WatchedEvent 包含了每個事件的三個基本屬性:

 

通知狀態(keeperState),事件類型(EventType)和節點路徑(path)

 

event 異步發送

 

watcher 的通知事件從服務端發送到客戶端是異步的。

 

先註冊再觸發

 

Zookeeper 中的 watch 機制,必須客戶端先去服務端註冊監聽,這樣事件發

 

送纔會觸發監聽,通知給客戶端。

 

2.6.二、通知狀態和事件類型

同一個事件類型在不一樣的通知狀態中表明的含義有所不一樣,下表列舉了常見

 

的通知狀態和事件類型。

KeeperState

EventType

觸發條件

說明

 

None

(-1)

客戶端與服務端成功創建鏈接

 

SyncConnected

(0)

NodeCreated

(1)

Watcher 監聽的對應數據節點被建立

 

NodeDeleted

(2)

Watcher 監聽的對應數據節點被刪除

此時客戶端和服務器處於鏈接狀態

NodeDataChanged

3

Watcher 監聽的對應數據節點的數據內容 發生變動

 

NodeChildChanged

(4)

Wather 監聽的對應節點的子節點數據列表發生變動

 

 

Disconnected

(0)

None

(-1)

客戶端與zookeeper服務器斷開鏈接

此時客戶端和服務器處於斷開鏈接狀態

Expired

(-112)

Node

(-1)

會話超時

此時客戶端會話失效,一般同時也會收到SessionExpiredException 異常

AuthFailed

(4)

None

(-1)

一般有兩種狀況

1:使用錯誤的

schema 進行權限檢查

2:SASL 權限檢查失敗

一般同時也會收到AuthFailedException 異常

 

其中鏈接狀態事件(type=None, path=null)不須要客戶端註冊,客戶端只要

 

有須要直接處理就好了。

 

 

2.6.三、shell客戶端設置watch機制

設置節點數據變更監聽:

 

經過另外一個客戶端更改節點數據:

 

此時設置監聽的節點收到通知:

 

 

2.七、zookeeper的javaAPI

Zookeeper 是在 Java 中客戶端主類,負責創建與 zookeeper 集羣的會話,並提供方法進行操做。org.apache.zookeeper.Watcher

Watcher 接口表示一個標準的事件處理器,其定義了事件通知相關的邏輯,

 

包含 KeeperState 和 EventType 兩個枚舉類,分別表明了通知狀態和事件類型,

 

同時定義了事件的回調方法:process(WatchedEvent event)。

 

process 方法是 Watcher 接口中的一個回調方法,當 ZooKeeper 向客戶端發

 

送一個 Watcher 事件通知時,客戶端就會對相應的 process 方法進行回調,從而

 

實現對事件的處理。

 

2.7.一、建立java工程,導入jar包

建立maven  java工程,導入jar包

<!-- <repositories>

    <repository>

      <id>cloudera</id>

      <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>

    </repository>

  </repositories> -->

   <dependencies>

       <dependency>

            <groupId>org.apache.curator</groupId>

            <artifactId>curator-framework</artifactId>

            <version>2.12.0</version>

        </dependency>

        <dependency>

            <groupId>org.apache.curator</groupId>

            <artifactId>curator-recipes</artifactId>

            <version>2.12.0</version>

        </dependency> 

       <dependency>

           <groupId>com.google.collections</groupId>

           <artifactId>google-collections</artifactId>

           <version>1.0</version>

       </dependency>

  </dependencies>

  <build>

       <plugins>

           <!-- java編譯插件 -->

           <plugin>

              <groupId>org.apache.maven.plugins</groupId>

              <artifactId>maven-compiler-plugin</artifactId>

              <version>3.2</version>

              <configuration>

                  <source>1.8</source>

                  <target>1.8</target>

                  <encoding>UTF-8</encoding>

              </configuration>

           </plugin>

       </plugins>

    </build>

 

2.7.二、節點的操做

建立永久節點

/**

     * 建立永久節點

     * @throws Exception

     */

    @Test

    public void createNode() throws Exception {

      RetryPolicy retryPolicy = new  ExponentialBackoffRetry(1000, 1);

//獲取客戶端對象

       CuratorFramework client = CuratorFrameworkFactory.newClient("192.168.221.100:2181,192.168.221.110:2181,192.168.221.120:2181", 1000, 1000, retryPolicy);

//調用start開啓客戶端操做

       client.start();

    //經過create來進行建立節點,而且須要指定節點類型

client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/hello3/world");

client.close();

    }

建立臨時節點

    /**

     * 建立臨時節點

     * @throws Exception

     */

    @Test

    public void createNode2() throws Exception {

      RetryPolicy retryPolicy = new  ExponentialBackoffRetry(3000, 1);

       CuratorFramework client = CuratorFrameworkFactory.newClient("node01:2181,node02:2181,node03:2181", 3000, 3000, retryPolicy);

       client.start();

client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath("/hello5/world");

       Thread.sleep(5000);

       client.close();

    }

   

修改節點數據

   

    /**

     * 節點下面添加數據與修改是相似的,一個節點下面會有一個數據,新的數據會覆蓋舊的數據

     * @throws Exception

     */

    @Test

    public void nodeData() throws Exception {

      RetryPolicy retryPolicy = new  ExponentialBackoffRetry(3000, 1);

       CuratorFramework client = CuratorFrameworkFactory.newClient("node01:2181,node02:2181,node03:2181", 3000, 3000, retryPolicy);

       client.start();

       client.setData().forPath("/hello5", "hello7".getBytes());

       client.close();

    }

節點數據查詢

    /**

     * 數據查詢

     */

    @Test

    public void updateNode() throws Exception {

      RetryPolicy retryPolicy = new  ExponentialBackoffRetry(3000, 1);

       CuratorFramework client = CuratorFrameworkFactory.newClient("node01:2181,node02:2181,node03:2181", 3000, 3000, retryPolicy);

       client.start();

       byte[] forPath = client.getData().forPath("/hello5");

       System.out.println(new String(forPath));

       client.close();

    }

   

   

節點watch機制

    /**

     * zookeeper的watch機制

     * @throws Exception

     */

    @Test

    public void watchNode() throws Exception {

       RetryPolicy policy = new ExponentialBackoffRetry(3000, 3);

       CuratorFramework client = CuratorFrameworkFactory.newClient("node01:2181,node02:2181,node03:2181", policy);

       client.start();

       // ExecutorService pool = Executors.newCachedThreadPool(); 

            //設置節點的cache 

            TreeCache treeCache = new TreeCache(client, "/hello5"); 

            //設置監聽器和處理過程 

            treeCache.getListenable().addListener(new TreeCacheListener() { 

                @Override 

                public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { 

                    ChildData data = event.getData(); 

                    if(data !=null){ 

                        switch (event.getType()) {

                        case NODE_ADDED

                            System.out.println("NODE_ADDED : "+ data.getPath() +"  數據:"+ new String(data.getData())); 

                            break

                        case NODE_REMOVED

                            System.out.println("NODE_REMOVED : "+ data.getPath() +"  數據:"+ new String(data.getData())); 

                            break

                        case NODE_UPDATED

                            System.out.println("NODE_UPDATED : "+ data.getPath() +"  數據:"+ new String(data.getData())); 

                            break

                             

                        default

                            break

                        } 

                    }else

                        System.out.println( "data is null : "+ event.getType()); 

                    } 

                } 

            }); 

            //開始監聽 

            treeCache.start(); 

            Thread.sleep(50000000);

    }

相關文章
相關標籤/搜索