Zookeeper單機以及集羣的使用

 

1.1 Zookeeper介紹

Zookeeper是分佈式應用程序的協調服務框架,是Hadoop的重要組件。ZK要解決的問題:

1.分佈式環境下的數據一致性。

2.分佈式環境下的統一命名服務

3.分佈式環境下的配置管理

4.分佈式環境下的分佈式鎖

單臺機器使用的鎖:同步代碼塊、重入鎖。但是在分佈式環境這個鎖就發揮不出來作用。分佈鎖分爲共享鎖和排他鎖。

5.集羣管理問題

1.2  分佈式概念(集羣)

分佈式的思想就是人多幹活快,即用多臺機器同時處理一個任務。分佈式的編程和單機的編程 思想是不同的,隨之也帶來新的問題和挑戰。

1.3  分佈式編程容易出現的問題

1.死鎖2.活鎖。

活鎖定義:在程序裏,由於某些條件的發生碰撞,導致重新執行,再碰撞=》再執 行,如此循環往復,就形成了活鎖。活鎖的危害:多個線程爭用一個資源,但是沒有任何一個 線程能拿到這個資源。(死鎖是有一個線程拿到資源,但相互等待互不釋放造成死鎖),活鎖 是死鎖的變種。補充:活鎖更深層次的危害,很耗盡Cpu資源(在做無意義的調度)

擴展:zk是根據Google的一篇論文

《The Chubby lock service for loosely coupled distributed systems

3.需要考慮集羣的管理問題,需要有一套機制來檢測到集羣裏節點的狀態變化。(可以用心跳 機制來做,但zk不是用心跳機制來做的)

4.如果用一臺機器做集羣管理,存在單點故障問題,所以針對集羣管理,也需要形成一個集羣

5.管理集羣裏Leader的選舉問題(要根據一定的算法和規則來選舉),包括要考慮Leader掛掉 之後,如何從剩餘的follower裏選出Leader

6.分佈式鎖的實現,用之前學的重入鎖,同步代碼塊是做不了的

  1.4  Zookeeper的名字

動物園管理員

2 Zookeeper單機模式安裝

實現步驟:

1.      準備虛擬機

克隆一個純淨的虛擬機

連接克隆佔用的空間較少,但是母體壞掉的話,克隆出來的虛擬機將不能使用

2.安裝和配置jdk

3.上傳和安裝zk

  A.上傳目錄:/usr/soft  上傳 rz 或者使用ssh工具

  B.tar -zxvf zookeeper-3.4.7.tar.gz

 4.配置zk的配置文件

目錄結構:

bin  指令

conf 配置文件

lib  運行jar包庫

進入conf目錄,執行:

cp zoo_sample.cfg zoo.cfg

5.啓動zk

進入bin目錄,執行:

sh zkServer.sh start  或者./zkServer.sh start

可以通過jps指令查看活動的java進程

zk的進程是:QuorumPeerMain

6.進入zk客戶端,操作zk

進入bin目錄,執行:  ./zkCli.sh

 

關閉zk服務:

sh zkServer.sh stop  或者

jps查看zk的進程id

kill -15  zk進程id

 

3   Zookeeper指令與數據結構

Zk數據結構

 

1.      ZK有一個最開始的節點

2.      ZK的節點叫做znode節點

3.      每個znode節點都可存儲數據

4.      每個znode節點都可創建自己的子節點

5.      多個znode節點共同形成了znode樹

6.      Znode樹的維繫實在內存中,目的是供用戶快速的查詢

7.      每個znode節點都是一個路徑(通過路徑來定位這個節點)

8.      每個路徑名都是唯一的。

 

 

ZK指令

指令

示例

ls查看指令

ls /

create創建節點指令,注意,在創建節點時,要分配初始數據。

create /zk01  ''

create /zk02 'hello'

get查看節點數據指令

hello  數據

cZxid = 0x2

ctime = Mon May 15 05:58:32 PDT 2017創建節點的時間戳

mZxid = 0x2

mtime = Mon May 15 05:58:32 PDT 2017修改此節點數據的最新時間戳

pZxid = 0x2

cversion = 0

dataVersion = 0數據版本號,每當數據發生編號,版本號遞增1

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 5數據大小

numChildren = 0子節點個數

get /zk02

set更新節點數據指令(執行後mtime、dataVersion可定會放生變化,dataLength可能會變化)

set /zk01 hellozk

delete刪除節點:如果存在子節點,不讓刪除。

只有刪除子節點後才能刪除。

delete /zk01

create指令補充:

1.       創建子節點

2.       Zk節點分四種類型:分別是:

普通持久節點:

普通臨時節點:創建此臨時節點的客戶端失去和zk連接後,此節點消失.zk是通過臨時節點監控哪個服務器掛掉的。

 

順序持久節點:會根據用戶指定的節點路徑,自動分配一個遞增的順序號。(順序節點實現分佈式鎖的效果,服務器1搶到zk05分配zk050001,服務器2搶到zk05分配zk050002)

順序臨時節點:

1.       create /zk01/node01 hello

2.        

2.1.create /zk01 hello

2.2. create –e /zk02 abc
2.3.create –s /zk03 abc

2.4.create –s -e /zk05 abcd

zk050000000003

再創建一個就是:

zk050000000004

quit退出zk客戶端

 

 

 

4   Zk API

4.1 項目搭建

實現步驟:

1.      關閉虛擬機的防火牆 service iptables stop(臨時關閉)

永久關閉:chkconfigiptables  off

2.      創建一個maven工程

修改maven工程的幾個參數

(1).修改默認的jdk,從1.5改爲1.7

(2).修改JavaCompiler版本,從1.5改爲1.7

點擊Apply->OK

(3).從提供的資料中將pom.xml文件拷貝過來替換項目中的同名文件。

zk需要的核心jar包

環境就搭建好了,接下來我們建測試類。(刪除不需要的App.java)

4.2 maven補充

 

學生機由於不能聯網,所以maven可能不能用,如果maven不能用:

1、  將apache-maven-3.3.1.zip解壓的D:\Tool

2、  將zebra_mvnrepository(私服)拷貝到D:\Tool

3、  修改D:\Tool\apache-maven-3.3.1\conf\settings.xml

<localRepository>D:\Tool\zebra_mvnrepository</localRepository>

修改該屬性後,表示只要使用該maven的所有項目下載時全部使用該私服。

4、  修改Eclipse使用本地的maven

5、  修改maven使用的配置文件,指向私服

擴展閱讀:maven超級pom(最頂層的父pom.xml)

maven-model-builder-3.3.1.jar->org\apache\maven\model\pom-4.0.0.xml 

<directory>${project.basedir}/target</directory>

<!—發佈完後的項目保存位置-->

<outputDirectory>${project.build.directory}/classes</outputDirectory>

<finalName>${project.artifactId}-${project.version}</finalName>    <testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>

<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>

<scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>

<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>

 

<repositories>

    <repository>

      <id>central</id>

      <name>Central Repository</name>

      <url>https://repo.maven.apache.org/maven2</url>

      <layout>default</layout>

      <snapshots>

        <enabled>false</enabled>

      </snapshots>

    </repository>

  </repositories>

 

  <pluginRepositories>

    <pluginRepository>

      <id>central</id>

      <name>Central Repository</name>

      <url>https://repo.maven.apache.org/maven2</url>

      <layout>default</layout>

      <snapshots>

        <enabled>false</enabled>

      </snapshots>

      <releases>

        <updatePolicy>never</updatePolicy>

      </releases>

    </pluginRepository>

  </pluginRepositories>

將以上兩個節點指定默認的中央倉庫,可以將之拷貝不同項目的pom.xml文件,分別修改,可以實現不同項目引用不同私服或公服。

 

4.3 API演示

4.3.1   編寫測試類,測試連接

    /**connectString連接zk服務器的ip(192.168.80.50)和port (2181)

     * sessionTimeout:回話的超時時間,單位爲毫秒

     * watcher:監聽器對象

     * @throws Exception

     */

    @Test

    public void testConect() throws Exception{

       ZooKeeper zk = new ZooKeeper("192.168.80.50:2181", 3000,

              new Watcher() {

                  @Override

                  public void process(WatchedEvent event) {

                     System.out.println("連接成功");

                  }

              });

       while(true);

    }

}

爲何添加while(true)就可以,原因是:

zk連接是一個非阻塞連接方法,連接還沒有來的急建立,該線程已經結束。

在連接成功後,使用zk.create(…)或zk.get(),那麼如何保證執行到該行時保證連接成功,可以使用閉鎖。

public void testConect() throws Exception{

       final CountDownLatch cdl = new CountDownLatch(1);

       ZooKeeper zk = new ZooKeeper("192.168.80.50:2181", 3000,

              new Watcher() {

                  @Override

                  public void process(WatchedEvent event) {

                     System.out.println("連接成功");

                     cdl.countDown();

                  }

              });

       //while(true);

       cdl.await();

    }

 

4.3.2   編寫測試創建節點

@Test

    publicvoid testCreate() throws Exception{

       final CountDownLatch cdl = new CountDownLatch(1);

       ZooKeeper zk = new ZooKeeper("192.168.80.50:2181", 3000,

              new Watcher() {

                  @Override

                  publicvoid process(WatchedEvent event) {

                     System.out.println("連接成功");

                     cdl.countDown();

                  }

              });

       cdl.await();

       //path:節點路徑

       //data:節點的內容

       //acl:節點的權限

       //createMode:節點的類型

       //PERSISTENT(persistent):普通持久節點

       //EPHEMERAL(ephemeral):普通臨時節點

       //PERSISTENT_SEQUENTIAL:順序持久節點

       //EPHEMERAL_SEQUENTIAL:順序臨時節點

        zk.create("/zk02","hellozk".getBytes(),

               Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL);

        while(true);

    }

如果創建不成功,有可能是該節點已經存在了。

4.3.3   測試get方法

    @Test

    public void testGet() throws Exception{

       ……//省略zk的創建。

       //path:獲取節點的地址

       //watcher監聽器

       //stat:查詢後,會將節點的其它信息封裝stat中

       Stat stat = new Stat();

       byte[] data = zk.getData("/zk01", new Watcher() {

           @Override

           public void process(WatchedEvent event) {

              System.out.println("獲取到數據");

           }

       }, stat);

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

       System.out.println(stat);

    }

 

4.3.4   測試set

  @Test

    public void testSet() throws Exception{

       ……

       //path:修改節點的地址

       //data:修改後節點的內容。

       //version:修改節點對應的版本號

       zk.setData("/zk01", "you can't sleep".getBytes(), -1);

       while(true);

    }

 

4.3.5   獲取子節點

@Test

    publicvoid testGetChild() throws Exception{

       ……

       //注意:statzk01的節點信息

       Stat stat = new Stat();

       List<String> paths = zk.getChildren("/zk01", null,stat);

       for (String path : paths) {

           System.out.println(path);

       }

       System.out.println(stat);

    }

 

4.3.6   手動刪除節點

@Test

    publicvoid testDelete() throws Exception{

       ……

       /**path:刪除的節點路徑

        * version:節點的版本,-1支持各種版本號

        * 刪除節點使用的場景不是太多,可以創建臨時節點,連接斷了之後,

        * 節點會自動被刪除。

        * 注意:只用空節點才能刪除。

        */

       zk.delete("/zk01", -1);

    }

4.3.7   觀察節點數據變化

@Test

    public void testGetDataWatcher() throws Exception{

       ……

       zk.getData("/zk01", new Watcher() {

           @Override

           public void process(WatchedEvent event) {

              System.out.println("數據放生變化");

           }

       }, null);

       while(true);

    }

運行程序後,在終端上執行以下命令:

set /zk01 newdata

控制檯輸出“數據發生變化”

目前只能監聽一次,如何實現永久監聽:

    @Test

    publicvoid testGetDataWatcher() throws Exception{

       ……

 

       while(true){

           final CountDownLatch cdl2 = new CountDownLatch(1);

           zk.getData("/zk01", new Watcher() {

              @Override

              publicvoid process(WatchedEvent event) {

                  System.out.println("數據放生變化");

                  cdl2.countDown();

              }

           }, null);

           cdl2.await();

       }

    }


4.3.8   監聽子節點變化(創建或刪除)

@Test

    publicvoid testGetChildWatcher() throws Exception{

       ……

 

       zk.getChildren("/zk01",new Watcher(){

           @Override

           publicvoid process(WatchedEvent event) {

               if(event.getType()==EventType.NodeChildrenChanged){

                  System.out.println("子節點發生了變化");

              }

           }

       });

       while(true);

    }

運行程序,終端上執行以下命令(在/zk01下創建子節點)

create /zk01/zk02 jxf

控制檯輸出“子節點變化”

4.3.9   監聽節點刪除

@Test

publicvoid testDeleteWatcher() throws Exception{

    ……

    zk.exists("/zk030000000004",new Watcher(){

       @Override

       publicvoid process(WatchedEvent event) {

           if(event.getType()==EventType.NodeDeleted){

              System.out.println("節點被刪除。。。");

           }

       }

    });

    while(true);

}

運行程序,在終端上執行刪除:delete /zk02

控制檯輸出:節點被刪除

 

4.3.10  監聽創建節點的相關信息

    @Test

    publicvoid testCreateCallBack() throws Exception{

       ……

       /**path:節點路徑

        * data:節點內容

        * acl:節點的相關權限

        * createMode:節點的類型

        * ctx:傳入的附件

        */

       zk.create("/zk04/node1", "Kill sleeper".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT,

              new StringCallback(){

                  @Override

                  publicvoid processResult(intrc, String path, Object ctx, String name) {

                     //int rc:狀態碼  0代表創建成功,-110 代表創建失敗

                     //String path:用戶指定的路徑

                     //Object ctx:傳入的附件

                     //String name:實際創建節點的路徑名

                      System.out.println("rc:"+rc+",path:"+path+",ctx:"+ctx+",name:"+name);

                  }

          

       },"over" );

       while(true);

    }

集羣搭建和配置

 

1.      克隆三臺空虛擬機(含有lrzsz),修改網絡ip,並關閉虛擬機的防火牆

臨時關閉:service iptables stop

永久關閉:chkconfig iptables  off

2.      安裝和配置jdk

3.      安裝和配置zookeeper

mkdir /use/soft

tar –zxvf /use/soft/zookeeper-3.4.7.tar.gz

[root@localhostconf]# cp zoo_sample.cfg  zoo.cfg

4.      配置zoo.cfg

配置說明:

tickTime=2000  心跳間隔週期  毫秒。

initLimit=10初始連接超時閾值=10*tickTime。指的是follower初始連接leader的超時時間。 如果網絡環境不好,適當調大。  

syncLimit=5連接超時閾值=syncLimit*tickTime。指的是follower和leader做數據交互的超時時間。如果網絡環境不好,適當調大。

dataDir=/usr/soft/zookeeper-3.4.7/tmp  dataDir數據目錄指的是zookeeper znode樹的持久化目錄,

server.1=192.168.80.51:2888:3888

server.2=192.168.80.52:2888:3888

server.3=192.168.80.53:2888:3888

server後的數字是選舉id,在選舉過程中會用到。注意:數字一定要能比較出大小。

2888 端口原子廣播端口,可以自定義

3888 端口選舉端口,可以自定義

在zk安裝目錄下創建tmp文件,創建myid文件(名字固定),並編輯當前虛擬機的選舉id

   Mkdir /usr/soft/zookeeper-3.4.7/tmp

   tmp#vim myid    (內容爲1)

遠程拷貝zk的安裝目錄到zk2、zk3上

[root@localhostsoftware]# scp -r zookeeper-3.4.7/ [email protected]:/home/software

[root@localhostsoftware]# scp -r zookeeper-3.4.7/ [email protected]:/home/software

分別修改myid,zk2->2,zk3->3

如果拷貝不過去或者拷貝太慢,可以先打包並壓縮:

[root@localhostsoftware]# tar -zcvf zk.tar.gz zookeeper-3.4.7/

然後在scp

然後在解壓:[root@localhost home]# tar -zxvf zk.tar.gz

5.啓動zk集羣測試

分別啓動zk

[root@localhost bin]# ls

README.txt    zkCli.cmd  zkEnv.cmd  zkServer.cmd

zkCleanup.sh  zkCli.sh   zkEnv.sh   zkServer.sh

[root@localhost bin]# ./zkServer.sh start

ZooKeeper JMX enabled by default

Using config: /usr/soft/zookeeper-3.4.7/bin/../conf/zoo.cfg

Starting zookeeper ... STARTED

[root@localhost bin]# jps

3490 Jps

3464 QuorumPeerMain

 

[root@localhostbin]# ./zkServer.sh status

ZooKeeper JMX enabled by default

Using config: /usr/soft/zookeeper-3.4.7/bin/../conf/zoo.cfg

Mode: follower

如果啓動順序爲zk1->zk2->zk3,通常zk2爲leader。

Leader是如何選舉出來的?

6   選舉機制

6.1 Zookeeper事務概念 

1.每一個寫操作都是一個事務,每一個事務都用一個事務id來代表,叫:zxid

2.zxid 是全局唯一,並且全局遞增的。作用就是可以根據最大事務id,找到最新的事務

6.2 Zookeeper選舉機制

 

6.2.1   選舉分兩個階段

1.      數據恢復階段:

 當zk服務器啓動時,會先從本地磁盤找到本機的最大事務id。

2.      選舉階段:

zk服務器會提交選舉協議 1.Zxid(最大事務id) 2.本機的選舉id(myid文件裏的數字) 3.邏輯時鐘值  記錄當前的選舉輪數,確保每個zk在同一輪選舉中 4.當前zk服務器狀態,分4種: Looking=>選舉階段 Following=>當小弟階段 Leading=>當領導階段 Observering=>觀察者階段

6.2.2   選舉的pk原則

1.首先比較最大事務id,Zxid,誰大誰當領導

2.如果Zxid比較不出來,比較myid(選舉id),誰大誰當領導

3.選舉的前提滿足過半同意

 

6.2.3   Leader選舉出來之後

Leader身上肯定是有最新數據的(有最大事務id的),所以首先做的就是原子廣播(通過原子 廣播端口)。 原子廣播的目的就是爲了確保數據一致性(即客戶端無論通過哪個zk服務器查看數據,數據都是一樣的)

 目的二就是爲了防止leader掛掉之後,數據的丟失問題

對於事務更新,Leader會通過原子廣播徵詢其他follower,只要滿足過半同意機制,事務才能被更新

 

針對下圖,Leader掛掉之後,第二個機器成爲Leader

 

針對下圖,不滿足過半機制,集羣就工作不了了

 

修改testCreateCallBack()方法

ZooKeeper zk=new ZooKeeper("192.168.80.51:2181,

192.168.80.52:2181,192.168.80.53:2181",3000,new Watcher(){…

測試發現,三臺zk上都創建了該節點。

 

Zxid 最大事務id 是全局的, cZxid、mZxid、pZxid是針對某個節點路徑而言的。

擴展Zookeeper的選舉機制根據Paxos 算法來實現。 Paxos算法解決的問題:在分佈式環境下就某一個決議達成一致性問題。 Paxos算法存在活鎖問題,Zk用的是FastPaxos算法,解決了活鎖問題。

 

  • czxid. 節點創建時的zxid.
  • mzxid. 節點最新一次更新發生時的zxid.
  • cversion. 其子節點的更新次數.
  • aclVersion. 節點ACL(授權信息)的更新次數.
  • ephemeralOwner. 如果該節點爲ephemeral節點, ephemeralOwner值表示與該節點綁定的session id. 如果該節點不是ephemeral節點, ephemeralOwner值爲0. 至於什麼是ephemeral節點, 請看後面的講述.
  • dataLength. 節點數據的字節數.
  • numChildren. 子節點個數.

 

6.2.4 zxid

znode節點的狀態信息中包含czxid和mzxid, 那麼什麼是zxid呢?

ZooKeeper狀態的每一次改變, 都對應着一個遞增的Transaction id, 該id稱爲zxid. 由於zxid的遞增性質, 如果zxid1小於zxid2, 那麼zxid1肯定先於zxid2發生. 創建任意節點, 或者更新任意節點的數據, 或者刪除任意節點, 都會導致Zookeeper狀態發生改變, 從而導致zxid的值增加

6.2.5   session

在client和server通信之前, 首先需要建立連接, 該連接稱爲session.連接建立後, 如果發生連接超時, 授權失敗, 或者顯式關閉連接, 連接便處於CLOSED狀態, 此時session結束.

6.2.6   節點類型

講述節點狀態的ephemeralOwner字段時, 提到過有的節點是ephemeral節點, 而有的並不是. 那麼節點都具有哪些類型呢? 每種類型的節點又具有哪些特點呢?
persistent.persistent節點不和特定的session綁定, 不會隨着創建該節點的session的結束而消失, 而是一直存在, 除非該節點被顯式刪除.
ephemeral.ephemeral節點是臨時性的, 如果創建該節點的session結束了, 該節點就會被自動刪除. ephemeral節點不能擁有子節點. 雖然ephemeral節點與創建它的session綁定, 但只要該該節點沒有被刪除, 其他session就可以讀寫該節點中關聯的數據. 使用-e參數指定創建ephemeral節點.

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章