Zookeeper

 

一、概述

工做機制

 

協調整個框架運行;但又處於背景版的角色;java

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

Zookeeper=文件系統+通知機制;算法

特色:

集羣的數量都是奇數個;(3臺和4臺的容錯機制(掛幾臺機器仍是能夠照樣運行)是同樣的,都是1臺;4臺太消耗資源)apache

數據結構

既是文件夾又是文件,叫znode;vim

應用:同步數據;服務器

統一命名服務、統一配置管理、統一集羣管理、服務器節點動態上下線、軟負載均衡等。網絡

 source /etc/profile &&session

 二、搭建集羣

下載並把壓縮包上傳到/opt/software 目錄中數據結構

https://zookeeper.apache.org/併發

1. 解壓到指定目錄 [kris@hadoop101 software]$ tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/ 2. 將/opt/module/zookeeper-3.4.10/conf這個路徑下的zoo_sample.cfg修改成zoo.cfg更名   把conf文件夾下配置文件改個名字 mv zoo_sample.cfg zoo.cfg
3. 打開zoo.cfg文件,修改dataDir路徑: 改路徑   
編輯zoo.cfg,配置datadir dataDir=/opt/module/zookeeper-3.4.10/zkData 4. 在/opt/module/zookeeper-3.4.10/這個目錄上建立zkData文件夾   [kris@hadoop101 zookeeper-3.4.10]$ mkdir zkData 4. 配置集羣機器,每臺機器分配一個不一樣的Serverid;在zoo.cfg文件末尾添加如下: 添加serverid server.1=hadoop101:2888:3888 server.2=hadoop102:2888:3888 server.3=hadoop103:2888:3888 5. 在zkData文件夾裏新建一個myid文件,內容是本機的Serverid;依次在各個集羣的服務器中添加serverid;
  vim myid ---> 添加 1
6. 配置了一下Zookeeper的LogDIR:配置bin/zkEnv.sh文件 ZOO_LOG_DIR="."改成自定義的日誌目錄/opt/module/zookeeper-3.4.10/logs

7. 使用腳本羣發;而後把各個server的id手動改了;在myid文件中
  xsync /opt/module/zookeeper-3.4.10 8. 啓動:   
bin/zkServer.sh start   查看進程是否啓動   [kris@hadoop101 zookeeper-3.4.10]$ jps ##每一個進程是來提供服務的;   4020 Jps   4001 QuorumPeerMain   查看狀態:   [kris@hadoop101 zookeeper-3.4.10]$ bin/zkServer.sh status 9. 啓動客戶端:   [kris@hadoop101 zookeeper-3.4.10]$ bin/zkCli.sh ##客戶端來鏈接集羣;

    WATCHER::

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

    [kris@hadoop101 zookeeper-3.4.10]$ jps
    3248 ZooKeeperMain   ##服務端
    3076 QuorumPeerMain  ##運行的客戶端
    3291 Jps

 10. 退出客戶端:   [zk: localhost:2181(CONNECTED) 0] quit
  中止Zookeeper
  [kris@hadoop101 zookeeper-3.4.10]$ bin/zkServer.sh stop

 

三、客戶端命令行操做

獲取根節點下面的全部子節點,使用ls / 命令便可 也可使用ls2 / 命令查看 獲取節點的數據內容和屬性,可以使用以下命令:get [zk: localhost:2181(CONNECTED) 15] ls /test1 [childNode, child1] [zk: localhost:2181(CONNECTED) 16] ls2 /test1 [childNode, child1] cZxid = 0xa00000014 ctime = Mon Jan 28 15:05:30 CST 2019 mZxid = 0xb00000044 mtime = Mon Jan 28 21:37:40 CST 2019 pZxid = 0xb00000046 cversion = 14 dataVersion = 4 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 9 numChildren = 2 [zk: localhost:2181(CONNECTED) 17] get /test1 zookeeper cZxid = 0xa00000014 ctime = Mon Jan 28 15:05:30 CST 2019 mZxid = 0xb00000044 mtime = Mon Jan 28 21:37:40 CST 2019 pZxid = 0xb00000046 cversion = 14 dataVersion = 4 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 9 numChildren = 2 使用set命令,能夠更新指定節點的數據內容; 相應的dataVersion會變

 

zookeeper 存數據+通知機制 1. 若是把服務器給kill了,它就會出現拒絕鏈接;默認鏈接的是本地的;若是啓動的時候指定了服務器,把它的服務器kill掉,它就會直接跳轉; [zk: localhost:2181(CONNECTED) 1] ls /zookeeper  ##這個是zookeeper自帶的節點; [quota] 2. [zk: localhost:2181(CONNECTED) 2] ls / watch ##watch是監視\節點的變化,有效性爲1次;  [test2, test40000000004, zookeeper, test1]   在另一臺客戶端上create: [zk: localhost:2181(CONNECTED) 0] create /data1 "heihei" ##建立節點的時候必定要告訴它數據是什麼 Created /data1 [zk: localhost:2181(CONNECTED) 3] WATCHER:: WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/   再建立第二個節點就沒反應了;zookeeper的觀察機制單次有效; 由於每一個節點都有一個watchingList,這時服務端就有觀察能力了;ls / watch註冊觀察的是根目錄,客戶端就會申請,zookeeper把數據仍進根目錄的watchingList;
若是watchingList發生變化,它就要去通知全部註冊過的客戶端,每通知一個就從list名單中劃掉; 3. 普通建立
-s 含有序列 -e 臨時(重啓或者超時消失) [zk: localhost:2181(CONNECTED) 2] create -s /data2 1235 Created /data20000000006 [zk: localhost:2181(CONNECTED) 3] create -e /linshi 001 Created /linshi
3. [zk: localhost:
2181(CONNECTED) 6] quit ### Quitting... 2019-01-27 00:29:27,946 [myid:] - INFO [main:ZooKeeper@684] - Session: 0x1688adaecec0001 closed 2019-01-27 00:29:27,954 [myid:] - INFO [main-EventThread:ClientCnxn$EventThread@519] - EventThread shut down for session: 0x1688adaecec0001 不一樣客戶端之間是互相獨立的,只有從本身建立的節點quit了,在另一個客戶端上這個節點(2s內)纔會消失; 4. ===> 一共4種節點類型:ephemeral sequential 兩兩組合; 有序持久-s; [zk: localhost:2181(CONNECTED) 1] create -s /order 111 Created /order0000000009 有序短暫 -s -e; [zk: localhost:2181(CONNECTED) 2] create -s -e /orderAndShort 222 Created /orderAndShort0000000010 無序持久; [zk: localhost:2181(CONNECTED) 3] create /long 333 Created /long 無序短暫-e; [zk: localhost:2181(CONNECTED) 4] create -e /short 444 Created /short

 監聽:監聽節點的路徑變化(set /test2 "zookeeper" ,改變它的值而監聽收不到的;監聽節點的增長、刪除 ls /test2 watch  

  監聽節點的內容 get /test2 watch      增長節點或刪除節點監聽是不會變化的,只有改變節點的內容如 set /test1 Hello纔會觸發watch

[zk: localhost:2181(CONNECTED) 2] get /test2 watch ##此時監聽的是節點的內容; 而ls 是監聽節點的路徑變化(增長| 刪除新節點了); abcd cZxid = 0x200000003 ctime = Sat Jan 26 15:23:14 CST 2019 mZxid = 0x200000003 mtime = Sat Jan 26 15:23:14 CST 2019 pZxid = 0x400000015 cversion = 1 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 4 numChildren = 1
[zk: localhost:2181(CONNECTED) 3] WATCHER:: WatchedEvent state:SyncConnected type:NodeDataChanged path:/test2 [zk: localhost:2181(CONNECTED) 4] create /test2/test0 123 ##改變節點的路徑,建立一個子節點;子節點的值沒變化; Created /test2/test0 [zk: localhost:2181(CONNECTED) 5] set /test2 QQ ##set是改變節點的值 cZxid = 0x200000003 ctime = Sat Jan 26 15:23:14 CST 2019 mZxid = 0x400000016 mtime = Sun Jan 27 00:53:19 CST 2019 pZxid = 0x400000015 cversion = 1 dataVersion = 1 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 2 numChildren = 1 [zk: localhost:2181(CONNECTED) 6] stat /test2 cZxid = 0x200000003 ##表示第幾回操做節點;2表服務端第2次啓動; 00000003當次啓動下的第幾回操做(16進制)建立了這個節點; ctime = Sat Jan 26 15:23:14 CST 2019 ##建立時間,long型時間戳; mZxid = 0x400000016 ##表服務器在第4次啓動時的00000016次修改了這個節點的數據; mtime = Sun Jan 27 00:53:19 CST 2019 #修改時間 pZxid = 0x400000015 ##表服務器在第4次啓動時第00000015次建立了子節點; cversion = 1     #子節點版本號;表test2節點下面子節點的變化號(刪除| 增長),1表變化了1次; dataVersion = 1 #表示子節點的數據變化號,修改的次數 aclVersion = 0 #access control list訪問控制列表,網絡版的權限控制;控制網絡上哪些人可訪問節點;0版本是均可以訪問的acl ephemeralOwner = 0x0 #非臨時節點 =0; #假如是臨時節點,0x0這裏就不會是0了;若是是臨時節點就會顯示出全部者;當你的全部者離線後它就天然消失了;它的值就是此刻的sessionid: 如0x16893294b0c0000 dataLength = 2 #數據長度 numChildren = 1 #子節點的數量; [zk: localhost:2181(CONNECTED) 1] create -e /testXXX 123 Created /testXXX [zk: localhost:2181(CONNECTED) 2] stat /testXXX cZxid = 0x400000019 ctime = Sun Jan 27 01:19:06 CST 2019 mZxid = 0x400000019 mtime = Sun Jan 27 01:19:06 CST 2019 pZxid = 0x400000019 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x1688adaecec0006 #sessionid = 0x1688adaecec0006 客戶端和服務器以後的會話id; dataLength = 3 numChildren = 0 [zk: localhost:2181(CONNECTED) 3] delete刪除沒有子節點的; rmr 是能夠刪除帶有子節點的; rmr /test2

 使用ssh啓動集羣:

兩種方法:

①是 source /etc/profile &&    ②zkEnv.sh文件夾中 配置下JAVA_HOME的環境變量:

#JAVA_HOME export JAVA_HOME=/opt/module/jdk1.8.0_144 export PATH=$PATH:$JAVA_HOME/bin
[kris@hadoop101 zookeeper-3.4.10]$ ssh hadoop103 /opt/module/zookeeper-3.4.10/bin/zkServer.sh status 
ZooKeeper JMX enabled by default Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg Error contacting service. It is probably not running. ###source這裏注意要有空格 [kris@hadoop101 zookeeper-3.4.10]$ ssh hadoop103 source /etc/profile && /opt/module/zookeeper-3.4.10/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg Mode: follower [kris@hadoop101 zookeeper-3.4.10]$ hadoop103要在zookeeper-3.4.10/bin目錄下的zkEnv.sh文件夾中 配置下JAVA_HOME的環境變量:
[kris@hadoop103 bin]$ vim zkEnv.sh #JAVA_HOME export JAVA_HOME
=/opt/module/jdk1.8.0_144 export PATH=$PATH:$JAVA_HOME/bin [kris@hadoop101 zookeeper-3.4.10]$ ssh hadoop103 /opt/module/zookeeper-3.4.10/bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg Mode: follower

 

四、API應用

節點訪問權限:

 

節點的類型選擇:ephemeral_sequential 、ephemeral、 persistent、persistent_sequential

 

 
public class Zookeeper{ private ZooKeeper zkClient; public static final String CONNECT_STRING = "hadoop101:2181,hadoop102:2181,hadoop103:2181"; public static final int SESSION_TIMEOUT = 2000; @Before public void before() throws IOException { //集羣地址;會話過時時間; 匿名內部類, 回調函數; 主進程不會停,根節點有變化經過回調函數告知 zkClient = new ZooKeeper(CONNECT_STRING, SESSION_TIMEOUT, new Watcher() { //建立ZooKeeper客戶端時 public void process(WatchedEvent event) { System.out.println("默認的回調函數"); //沒有監視任何節點,可寫可不寫  } }); } //1. 建立節點:
 @Test public void create() throws KeeperException, InterruptedException { String s = zkClient.create("/APITest", "123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);//節點的權限訪問;臨時有序節點  System.out.println(s); Thread.sleep(Long.MAX_VALUE); }

  @Test//2. 監聽只一次有效;監聽的是節點的變化--增長或刪除節點
  public void getChildren() throws KeeperException, InterruptedException {
   //sendThread負責通訊; eventThread負責監聽;當節點有變化eventThread調用默認的回調函數, 可自定義;
  List<String> children = zkClient.getChildren("/test1",true); //只監聽一次;
  for (String child : children) {
   System.out.println(child);
  }
  System.out.println("===============");
  Thread.sleep(Long.MAX_VALUE);
  }
  //3. 遞歸--> 可實現反覆調用watch( )  @Test public void getChildren() throws KeeperException, InterruptedException { List<String> children = zkClient.getChildren("/", new Watcher() { //watch: true 會監聽,調用默認的回調函數,監聽一次有效; // 還能夠寫new Watch 就不用默認的回調函數了; public void process(WatchedEvent watchedEvent) { try { System.out.println("本身的回調函數");  getChildren(); //可反覆監聽;監聽根目錄 watcher.process(pair.event); } catch (KeeperException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }); for (String child : children) { System.out.println(child); } System.out.println("=================="); } @Test //4. 可反覆調用,反覆監聽; public void testGet() throws KeeperException, InterruptedException { getChildren(); Thread.sleep(Long.MAX_VALUE); //主線程被阻塞,說明回調的時候不是主線程  } // 5. 判斷znode是否存在  @Test public void exist() throws KeeperException, InterruptedException { Stat stat = zkClient.exists("/zookeeper1", false); if (stat == null){ System.out.println("節點不存在"); }else{ System.out.println(stat.getDataLength()); } } }

 

五、監聽器原理

 

ClientCnxn.java: sendThread = new SendThread(clientCnxnSocket); //connect就是sendThread負責網絡鏈接通訊; eventThread = new EventThread();        //listener就是eventThread負責監聽 這裏建立了兩個子線程; class SendThread extends ZooKeeperThread public class ZooKeeperThread extends Thread public void start() { sendThread.start(); 由客戶端向zookeeper發送信息的線程; eventThread.start(); zookeeper發生變化來通知,由eventThread負責接收事件的變化;eventThread負責調用的回調函數,zookeeper發生了變化它把這個變化發給eventThread }

 六、ZAB協議

 Paxos算法

基於消息傳遞且具備高度容錯特性的一致性算法;多數原則;

消息傳遞有前後順序,數據同步難以實現;

ZAB協議(Paxos算法在Zookeeper中的實現)

Zookeeper--Atomic-Broadcast

Zookeeper怎麼保證數據的全局一致性?經過ZAB協議

 ① ZAB協議:崩潰恢復;正常執行寫數據;

 ② 沒leader選leader;有leader就幹活;

選舉機制

1)半數機制:集羣中半數以上機器存活,集羣可用。因此Zookeeper適合安裝奇數臺服務器。

2)Zookeeper雖然在配置文件中並無指定Master和Slave。可是,Zookeeper工做時,是有一個節點爲Leader,其餘則爲Follower,Leader是經過內部的選舉機制臨時產生的。

3)以一個簡單的例子來講明整個選舉的過程。

假設有五臺服務器組成的Zookeeper集羣,它們的id從1-5,同時它們都是最新啓動的,也就是沒有歷史數據,在存放數據量這一點上,都是同樣的。假設這些服務器依序啓動

(1)服務器1啓動,發起一次選舉。服務器1投本身一票。此時服務器1票數一票,不夠半數以上(3票),選舉沒法完成,服務器1狀態保持爲LOOKING;

(2)服務器2啓動,再發起一次選舉。服務器1和2分別投本身一票並交換選票信息:此時服務器1發現服務器2的ID比本身目前投票推舉的(服務器1)大,更改選票爲推舉服務器2。此時服務器1票數0票,服務器2票數2票,沒有半數以上結果,選舉沒法完成,服務器1,2狀態保持LOOKING

(3)服務器3啓動,發起一次選舉。此時服務器1和2都會更改選票爲服務器3。這次投票結果:服務器1爲0票,服務器2爲0票,服務器3爲3票。此時服務器3的票數已經超過半數,服務器3當選Leader。服務器1,2更改狀態爲FOLLOWING,服務器3更改狀態爲LEADING;

(4)服務器4啓動,發起一次選舉。此時服務器1,2,3已經不是LOOKING狀態,不會更改選票信息。交換選票信息結果:服務器3爲3票,服務器4爲1票。此時服務器4服從多數,更改選票信息爲服務器3,並更改狀態爲FOLLOWING;

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

 假設5臺機器同時啓動,5號當選;

選舉時判斷厲害的標準:

先比較 Zxid(服務器執行寫數據的次數,最新的Zxid表示服務器數據新舊的程度,Zxid越大表示服務器數據越新;)

若是Zxid相同再比較myid;

寫數據流程

讀數據,zookeeper全局數據一致;

 每一個Server節點都維護了一個待寫隊列;有寫請求不會當即寫,會加入待寫隊列;這個寫請求有可能成功也可能失敗;

新加入的寫操做的zxid 必定要大於服務器中本來有的zxid,以前寫過留下的 --->寫操做才能進入待寫隊列;(待寫隊列中都是沒有寫的;如本來的zxid爲3,新zxid爲6,再來一個zxid=5的會插入到6的前邊,隊列中是有序的)

算法推演過程:

① 成功:

Leader收到半數以上Server的成功信息,包括Leader本身;3臺服務器,有2臺贊成了,則Leader就會廣播,Server中待寫隊列的數據纔會寫成功;

(如執行set /data1 "Hello" ,在本身的Server節點zxid是最新,但在其餘server中卻不必定是最新的,由於網絡通訊有延遲,本地操做倒是很快的)

② 失敗:

  server1收到寫請求,交給leader,leader發給server1和server2;同時server2也收到寫請求,交給leader,leader也要發給server1和2;

按leader收到的順序是一、2,因爲網絡緣由,server1先收到1,再收到2;server2收到二、1;因而1就加入失敗;

  待寫隊列中有兩條寫請求zxid=6和zxid=7,同時轉發給leader,leader廣播給全部的server;結果7號你們先贊成accept了;leader就讓你們寫;

因爲網絡緣由,6才收到寫請求,此時最新的zxid=7是大於6的 ==>寫失敗;

leader先發送寫請求,再批准寫請求;發送的過程不必定收到成功信息,假如收到半數以上失敗的,寫就失敗了,leader就廣播你們把這個數據從待寫隊列中移除; 

③ 單個節點掉丟了:

5個節點;leader發送寫請求,有兩個節點不一樣意,3個節點贊成;leader廣播全部的server開始寫數據;原來不一樣意的兩個節點原地自殺,它倆就不對外提供服務了,它倆數據出現不一致的問題,跟集羣不一樣步了,而後它倆就去找leader按照它的zxid依次拉取數據把信息同步過來;

 

經過ZAB協議,在基於消息傳遞模型的狀況下,zookeeper才能保持全局數據的一致性;

寫請求先轉發給leader ---> leader要把寫請求轉發給全部的server, --->它們開始投票,贊成or不一樣意 --->leader統計票數發佈結果; --->廣播給各個server要麼寫要麼讓server把請求從隊列中移除;

 ④ Observer

  ④.1 觀察者;隨着集羣的擴張(數量| 橫向),寫數據越來越麻煩,寫效率變慢,讀服務的併發效率則是愈來愈高的;

爲了解決這種矛盾引入observer,只聽命令不投票; 對外可提供讀服務,不投票(寫請求是否成功它無論,它沒有投票權其餘都是同樣的);;

如3臺server,引入2臺observer,寫性能仍是由原來的3個決定,寫性能不能,可大幅度提高集羣的併發讀性能;

  ④.2 通常集羣是搭在數據中心內部,但有些大公司zookeeper集羣可能分佈在不一樣的數據中心當中;

如三個數據中心DC一、DC二、DC3,各個中心中有3個server,DC1中有一個leader; 

DC1中的3臺中1臺當leader,另外2個當fllower;DC二、DC3中的zookeeper6臺server當observer,它們不參與投票;

相關文章
相關標籤/搜索