Hadoop生態圈(五):Zookeeper

目錄

1 Zookeeper

1.1 概述

1.2 特點

1.3 數據結構

1.4 應用場景,

1.5 下載地址

2 zookeeper安裝部署

2.1 分佈式安裝部署

2.2 配置參數解讀

3 Zookeeper內部原理

3.1 選舉機制

3.2 節點類型

3.3 監聽原理

3.5 寫數據流程

4 zookeeper實戰

4.1 客戶端命令行操作

4.2 stat結構體

4.3 API應用


1 Zookeeper

1.1 概述

Zookeeper是一個開源的、分佈式的,爲分佈式應用提供協調服務的Apache項目

Zookeeper從設計模式角度來理解:是一個基於觀察者模式設計的分佈式服務管理框架,他負責存儲和管理大家都關心的數據,然後接受觀察者的註冊,一旦這些數據的狀態發生變化,Zookeeper就將通知已經再zookeeper上註冊的那些觀察者,通知其做出相應的反應,從而實現集羣中類似Master/Slave管理模式

簡單來說:Zookeeper = 文件系統(可以再zk上存儲數據)+ 通知機制

1.2 特點

  1. zookeeper:一個領導者(leader),多個跟隨者(follower)組成的集羣
  2. Leader負責進行投票的發起和決議,更新系統狀態
  3. Follower用於接收客戶端請求,並向客戶端返回結果,在選舉leader過程中參與投票
  4. 集羣中只要由半數以上節點存活,zookeeper集羣就可以正常工作,提供服務
  5. 全局數據一致:每個server保存一份相同的數據副本,client無論連接哪一個server,數據都是一致的
  6. 更新請求按順序進行,來自一個client的更新請求按其發送順序依次進行
  7. 數據更新原子性,一次數據跟新要麼全部成功,要麼全部失敗
  8. 實時性,在一定的時間範圍內,client能讀到最新的數據

1.3 數據結構

zookeeper數據模型的結構和Unix文件系統很類似,整體上可以看作爲一棵樹,每個節點稱爲一個ZNode

顯然,zookeeper集羣自身維護了一套數據結構,這個結構就是一個樹形結構,其上的每一個節點成爲“znode”,每個znode節點默認能夠存儲1MB的數據,每個znode節點都可以通過其路徑唯一標識,如圖所示

那zookeeper這顆“樹”有什麼特點呢? zookeeper的節點znode分爲兩種類型:

  • 短暫/臨時(ephemeral):當客戶端和服務端斷開連接之後,所創建的znode節點會自動刪除
  • 持久(persistent):當客戶端和服務端斷開連接後,所創建的znode節點不會刪除

1.4 應用場景,

提供的服務包括:分佈式消息同步和協調機制、服務器節點動態上下線、統一配置管理、負載均衡、集羣管理等,如圖

 

 

 

 

 

1.5 下載地址

官網:https://zookeeper.apache.org    (俗稱動物園管理員)

網盤:請點這裏        提取碼:yzpk 

 

 

 

 

2 zookeeper安裝部署

2.1 分佈式安裝部署

1. 集羣規劃

    在hadoop101、hadoop102和hadoop103三個節點上部署zookeeper

2. 解壓安裝

     將下載的zookeeper文件上傳到虛擬機上

    1. 解壓 zookeeper安裝包到/opt/module/目錄下 

[root@hadoop101 software]$ tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/

    2. 在 /opt/module/zookeeper-3.4.10/這個目錄下創建zkData  (後面有解釋)

    3. 重命名/opt/module/zookeeper-3.4.10/conf這個目錄下的zoo_sample.cfg爲zoo.cfg

3. 配置zoo.cfg文件

    1. 具體配置

dataDir=/opt/module/zookeeper-3.4.10/zkData

     增加如下配置:

    2. 配置參數解讀

server.A=B:C:D

A是一個數字,表示這個是第幾號服務器;

B是這個服務器的ip地址;

C是這個服務器與集羣中的Leader服務器交換信息的端口;

D是萬一集羣中的Leader服務器掛了,需要一個端口來重新進行選舉,選出一個新的Leader,而這個端口就是用來執行選舉時服務器相互通信的端口。

集羣模式下配置一個文件myid,這個文件在dataDir目錄下,這個文件裏面有一個數據就是A的值,Zookeeper啓動時讀取此文件,拿到裏面數據與zoo.cfg裏面的配置信息比較從而判斷到底是哪個server。

4. 集羣操作

    1. 在/opt/module/zookeeper-3.4.10/zkData目錄下創建一個myid的文件,並編輯該文件

touch myid

vim myid                    在文件中添加與server對應的編號:如1

    2. 拷貝配置好的zookeeper到另外兩臺機器上,並分別修改myid文件中的內容爲2、3

    3. 分別啓動zookeeper

[root@hadoop101 zookeeper-3.4.10]# bin/zkServer.sh start

[root@hadoop102 zookeeper-3.4.10]# bin/zkServer.sh start

[root@hadoop103 zookeeper-3.4.10]# bin/zkServer.sh start

    4. 查看狀態

[root@hadoop101 zookeeper-3.4.10]# bin/zkServer.sh status

JMX enabled by default

Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg

Mode: follower

[root@hadoop102 zookeeper-3.4.10]# bin/zkServer.sh status

JMX enabled by default

Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg

Mode: leader

[root@hadoop103 zookeeper-3.4.5]# bin/zkServer.sh status

JMX enabled by default

Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg

Mode: follower

2.2 配置參數解讀

解釋zoo.cfg文件中參數含義

1. tickTime: 通信心跳數,Zookeeper服務器心跳時間,單位毫秒

    Zookeeper使用的基本時間,服務器之間維持心跳的時間間隔,也就是每個tickTime時間都會發送一個心跳,報活

2. initLinit:LF初始通信時限

    集羣中的follower跟隨着服務器(F)與leader領導者服務器(L)之間初始連接時能容忍的最多心跳數,用它來界定集羣中的zookeeper服務器連接到 Leader的時限

3. syncLimit:LF同步通信時限

    集羣中的Leader與Follower之間的最大相應時間單位,假如響應超過syncLimit*tickTime,Leader認爲Follower掛掉,從服務器列表中刪除

   在運行過程中,Leader負責與ZK集羣中的所有機器進行通信,例如通過一些心跳檢測機制,來檢測機器的存活狀態。

   如果Leader發出心跳包在syncLimit之後,還沒有從Follower那收到響應,那麼就認爲i這個Follower已經不在線了

4. dataDir:數據文件目錄+數據持久化路徑

    保存內存數據庫快照信息的位置,如果沒有其他說明,更新的事務日誌也保存到數據庫。

5. clientPort:客戶端連接端口

    監控客戶端連接的端口

3 Zookeeper內部原理

3.1 選舉機制

1. 半數機制:集羣中半數以上存活,集羣可用,否則集羣處於癱瘓狀態不可用。

     爲什麼說zookeeper適合裝在奇數臺機器上 ?

原因:比如三個節點的zookeeper集羣,要想保證集羣可用,最多掛掉一個節點;如果是四個節點的zookeepe集羣,要想保證集羣可用,最多也只能掛掉一個節點,如果掛掉兩個就不滿足半數以上存活。所以說奇數個節點和偶數個節點對於zookeeper節點宕機的容忍度是相同的,所以選用奇數個節點,避免浪費資源

2. 選舉機制:

    zookeeper雖然在配置文件中並沒有指定leader和follower。但是在zookeeper集羣工作時,是有一個節點爲leader,其他節點爲follower,Leader是通過內部的選舉機制臨時產生的

     選舉機制以下面的例子說明

     假設有五臺服務器組成的zookeeper集羣,它們的id從1-5,同時它們都是最新啓動的。假設這些服務器依序啓動,來看看會發生什麼

 

(1)服務器1啓動,此時只有它一臺服務器啓動了,它發出去的報沒有任何響應,所以它的選舉狀態一直是LOOKING狀態。

(2)服務器2啓動,它與最開始啓動的服務器1進行通信,互相交換自己的選舉結果,由於兩者都沒有歷史數據,所以id值較大的服務器2勝出,但是由於沒有達到超過半數以上的服務器都同意選舉它(這個例子中的半數以上是3),所以服務器1、2還是繼續保持LOOKING狀態。

(3)服務器3啓動,根據前面的理論分析,服務器3成爲服務器1、2、3中的老大,而與上面不同的是,此時有三臺服務器選舉了它,所以它成爲了這次選舉的leader。

(4)服務器4啓動,根據前面的分析,理論上服務器4應該是服務器1、2、3、4中最大的,但是由於前面已經有半數以上的服務器選舉了服務器3,所以它只能接收當小弟的命了。

(5)服務器5啓動,同4一樣當小弟。

3.2 節點類型

1. Znode有兩種類型四種形式的目錄節點(默認是persistent)

    (1)持久化目錄節點(PERSISTENT)

客戶端與zookeeper斷開連接後,該節點依舊存在

    (2)持久化順序編號目錄節點(PERSISTENT_SEQUENTIAL)

客戶端與zookeeper斷開連接後,該節點依舊存在,只是Zookeeper給該節點名稱進行順序編號

    (3)臨時目錄節點(EPHEMERAL)

客戶端與zookeeper斷開連接後,該節點被刪除

    (4)臨時順序編號目錄節點(EPHEMERAL_SEQUENTIAL)

客戶端與zookeeper斷開連接後,該節點被刪除,只是Zookeeper給該節點名稱進行順序編號

2.創建znode時設置順序標識,znode名稱後會附加一個值,順序號是一個單調遞增的計數器,由父節點維護

3.在分佈式系統中,順序號可以被用於爲所有的事件進行全局排序,這樣客戶端可以通過順序號推斷事件的順序

3.3 監聽原理

 

 1. 監聽原理

  1. 首先要有一個main()線程
  2. 在main線程中創建Zookeeper客戶端,這時就會創建兩個線程,一個負責網絡連接通信(connect),一個負責監聽(listener)。
  3. 通過connect線程將註冊的監聽事件發送給Zookeeper。
  4. 在Zookeeper的註冊監聽器列表中將註冊的監聽事件添加到列表中。
  5. Zookeeper監聽到有數據或路徑變化,就會將這個消息發送給listener線程。
  6. listener線程內部調用了process()方法。

2. 常見的監聽

     1. 監聽節點數據的變化

get path [watch]

     2. 監聽節點增減的變化

ls path [watch]

3.5 寫數據流程

ZooKeeper 的寫數據流程主要分爲以下幾步:

  1. 1)比如 Client 向 ZooKeeper 的 Server1 上寫數據,發送一個寫請求。
  2. 2)如果Server1不是Leader,那麼Server1 會把接受到的請求進一步轉發給Leader,因爲每個ZooKeeper的Server裏面有一個是Leader。這個Leader 會將寫請求廣播給各個Server,比如Server1和Server2, 各個Server寫成功後就會通知Leader。
  3. 3)當Leader收到大多數 Server 數據寫成功了,那麼就說明數據寫成功了。如果這裏三個節點的話,只要有兩個節點數據寫成功了,那麼就認爲數據寫成功了。寫成功之後,Leader會告訴Server1數據寫成功了。
  4. 4)Server1會進一步通知 Client 數據寫成功了,這時就認爲整個寫操作成功。

4 zookeeper實戰

4.1 客戶端命令行操作

命令基本語法

功能描述

help

顯示所有操作命令

ls path [watch]

使用 ls 命令來查看當前znode中所包含的內容

ls2 path [watch]

查看當前節點數據並能看到更新次數等數據

create

普通創建

-s  含有序列

-e  臨時(重啓或者超時消失)

get path [watch]

獲得節點的值

set

設置節點的具體值

stat

查看節點狀態

delete

刪除節點

rmr

遞歸刪除節點

1.   啓動客戶端

[root@hadoop101 zookeeper-3.4.10]$ bin/zkCli.sh

2.   顯示所有操作命令

[zk: localhost:2181(CONNECTED) 1] help

3.   查看當前znode中所包含的內容

[zk: localhost:2181(CONNECTED) 0] ls /

[zookeeper]

4.查看當前節點數據並能看到更新次數等數據

[zk: localhost:2181(CONNECTED) 1] ls2 /

[zookeeper]

cZxid = 0x0

ctime = Thu Jan 01 08:00:00 CST 1970

mZxid = 0x0

mtime = Thu Jan 01 08:00:00 CST 1970

pZxid = 0x0

cversion = -1

dataVersion = 0

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 0

numChildren = 1

5.創建普通節點

[zk: localhost:2181(CONNECTED) 2] create /app1 "hello app1"

Created /app1

[zk: localhost:2181(CONNECTED) 4] create /app1/server101 "192.168.1.101"

Created /app1/server101

6.獲得節點的值

[zk: localhost:2181(CONNECTED) 6] get /app1

hello app1

cZxid = 0x20000000a

ctime = Mon Jul 17 16:08:35 CST 2017

mZxid = 0x20000000a

mtime = Mon Jul 17 16:08:35 CST 2017

pZxid = 0x20000000b

cversion = 1

dataVersion = 0

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 10

numChildren = 1

[zk: localhost:2181(CONNECTED) 8] get /app1/server101

192.168.1.101

cZxid = 0x20000000b

ctime = Mon Jul 17 16:11:04 CST 2017

mZxid = 0x20000000b

mtime = Mon Jul 17 16:11:04 CST 2017

pZxid = 0x20000000b

cversion = 0

dataVersion = 0

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 13

numChildren = 0

7.創建短暫節點

[zk: localhost:2181(CONNECTED) 9] create -e /app-emphemeral 8888

(1)在當前客戶端是能查看到的

[zk: localhost:2181(CONNECTED) 10] ls /

[app1, app-emphemeral, zookeeper]

(2)退出當前客戶端然後再重啓客戶端

[zk: localhost:2181(CONNECTED) 12] quit

[bigdata@hadoop104 zookeeper-3.4.10]$ bin/zkCli.sh

(3)再次查看根目錄下短暫節點已經刪除

[zk: localhost:2181(CONNECTED) 0] ls /

[app1, zookeeper]

8.創建帶序號的節點

(1)先創建一個普通的根節點app2

[zk: localhost:2181(CONNECTED) 11] create /app2 "app2"

(2)創建帶序號的節點

[zk: localhost:2181(CONNECTED) 13] create -s /app2/aa 888

Created /app2/aa0000000000

[zk: localhost:2181(CONNECTED) 14] create -s /app2/bb 888

Created /app2/bb0000000001

[zk: localhost:2181(CONNECTED) 15] create -s /app2/cc 888

Created /app2/cc0000000002

如果原節點下有1個節點,則再排序時從1開始,以此類推。

[zk: localhost:2181(CONNECTED) 16] create -s /app1/aa 888

Created /app1/aa0000000001

9.修改節點數據值

[zk: localhost:2181(CONNECTED) 2] set /app1 999

10.節點的值變化監聽

(1)在103主機上註冊監聽/app1節點數據變化

[zk: localhost:2181(CONNECTED) 26] get /app1 watch

(2)在102主機上修改/app1節點的數據

[zk: localhost:2181(CONNECTED) 5] set /app1  777

(3)觀察103主機收到數據變化的監聽

WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/app1

11.節點的子節點變化監聽(路徑變化)

(1)在103主機上註冊監聽/app1節點的子節點變化

[zk: localhost:2181(CONNECTED) 1] ls /app1 watch

[aa0000000001, server101]

(2)在102主機/app1節點上創建子節點

[zk: localhost:2181(CONNECTED) 6] create /app1/bb 666

Created /app1/bb

(3)觀察103主機收到子節點變化的監聽

WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/app1

12.刪除節點

[zk: localhost:2181(CONNECTED) 4] delete /app1/bb

13.遞歸刪除節點

[zk: localhost:2181(CONNECTED) 7] rmr /app2

14.查看節點狀態

[zk: localhost:2181(CONNECTED) 12] stat /app1

cZxid = 0x20000000a

ctime = Mon Jul 17 16:08:35 CST 2017

mZxid = 0x200000018

mtime = Mon Jul 17 16:54:38 CST 2017

pZxid = 0x20000001c

cversion = 4

dataVersion = 2

aclVersion = 0

ephemeralOwner = 0x0

dataLength = 3

numChildren = 2

4.2 stat結構體

  1. 1)czxid- 引起這個znode創建的zxid,創建節點的事務的zxid
  2. 每次修改ZooKeeper狀態都會收到一個zxid形式的時間戳,也就是ZooKeeper事務ID。
  3. 事務ID是ZooKeeper中所有修改總的次序。每個修改都有唯一的zxid,如果zxid1小於zxid2,那麼zxid1在zxid2之前發生。
  4. 2)ctime - znode被創建的毫秒數(從1970年開始)
  5. 3)mzxid - znode最後更新的zxid
  6. 4)mtime - znode最後修改的毫秒數(從1970年開始)
  7. 5)pZxid-znode最後更新的子節點zxid
  8. 6)cversion - znode子節點變化號,znode子節點修改次數
  9. 7)dataversion - znode數據變化號
  10. 8)aclVersion - znode訪問控制列表的變化號
  11. 9)ephemeralOwner- 如果是臨時節點,這個是znode擁有者的session id。如果不是臨時節點則是0。
  12. 10)dataLength- znode的數據長度
  13. 11)numChildren - znode子節點數量

4.3 API應用

首先導入下面的依賴

<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.10</version>
</dependency>
package com.zookeeper;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

public class ZkDemo {

    private String connectStr = "hadoop101:2181,hadoop102:2181,hadoop103:2181";
    private int sessionTimeout = 20000;

    private ZooKeeper zkClient;

    /**
     * 創建zookeeper客戶端
     */
    @Before
    public void testZk() throws Exception {
        zkClient = new ZooKeeper(connectStr, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println("event type:" + watchedEvent.getType() + "  path:" + watchedEvent.getPath());
                // 獲取子節點信息的同時,開啓對該節點的監控(監控子節點增加或者刪除)
                List<String> children = null;
                try {
                    children = zkClient.getChildren("/", true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 創建子節點
     * @throws Exception
     */
    @Test
    public void testCreateNode() throws Exception {
        zkClient.create("/ceshi","testValue".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    /**
     * 創建子節點並監聽
     * @throws Exception
     */
    @Test
    public void testGetChildren() throws Exception {
        // 獲取子節點信息的同時,開啓對該節點的監控(監控子節點增加或者刪除)
        List<String> children = zkClient.getChildren("/", true);
        for(String child: children) {
            System.out.println("=========>child:" + child);
        }
        // 線程阻塞
        Thread.sleep(Integer.MAX_VALUE);
    }

    /**
     * 判斷節點是否存在
     * @throws Exception
     */
    @Test
    public void testExists() throws Exception {
        Stat exists = zkClient.exists("/app1", false);
        System.out.println(exists);
    }
}

 

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