溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

zookeeper入門到實戰(zhàn)

發(fā)布時間:2020-08-03 01:34:58 來源:網(wǎng)絡(luò) 閱讀:886 作者:Java_老男孩 欄目:編程語言

一.zookeeper介紹

ZooKeeper 是一個開源的分布式協(xié)調(diào)服務(wù),由雅虎創(chuàng)建,是 Google Chubby 的開源實現(xiàn)。分布式應(yīng)用程序可以基于 ZooKeeper 實現(xiàn)諸如數(shù)據(jù)發(fā)布/訂閱負(fù)載均衡、命名服務(wù)分布式協(xié)調(diào)/通知、集群管理、Master 選舉、配置維護,名字服務(wù)分布式同步、分布式鎖和分布式隊列等功能。

數(shù)據(jù)模型ZooKeeper 允許分布式進(jìn)程通過共享的層次結(jié)構(gòu)命名空間進(jìn)行相互協(xié)調(diào),這與標(biāo)準(zhǔn)文件系統(tǒng)類似。名稱空間由 ZooKeeper 中的數(shù)據(jù)寄存器組成,稱為 Znode,這些類似于文件和目錄。與典型文件系統(tǒng)不同,ZooKeeper 數(shù)據(jù)保存在內(nèi)存中,這意味著 ZooKeeper 可以實現(xiàn)高吞吐量和低延遲

順序訪問:對于來自客戶端的每個更新請求,ZooKeeper 都會分配一個全局唯一的遞增編號。這個編號反應(yīng)了所有事務(wù)操作的先后順序,應(yīng)用程序可以使用 ZooKeeper 這個特性來實現(xiàn)更高層次的同步原語。這個編號也叫做時間戳zxidZooKeeper Transaction Id)。

可構(gòu)建集群:為了保證高可用,最好是以集群形態(tài)來部署 ZooKeeper,這樣只要集群中大部分機器是可用的(能夠容忍一定的機器故障),那么 ZooKeeper 本身仍然是可用的??蛻舳嗽谑褂?ZooKeeper 時,需要知道集群機器列表,通過與集群中的某一臺機器建立 TCP 連接來使用服務(wù)??蛻舳耸褂眠@個 TCP 鏈接來發(fā)送請求、獲取結(jié)果、獲取監(jiān)聽事件以及發(fā)送心跳包。如果這個連接異常斷開了,客戶端可以連接到另外的機器上。

zookeeper入門到實戰(zhàn)

  • 上圖中每一個 Server 代表一個安裝 ZooKeeper 服務(wù)的服務(wù)器。組成 ZooKeeper服務(wù)的服務(wù)器都會在內(nèi)存中維護當(dāng)前的服務(wù)器狀態(tài),并且每臺服務(wù)器之間都互相保持著通信。集群間通過 Zab 協(xié)議(Zookeeper Atomic Broadcast)來保持?jǐn)?shù)據(jù)的一致性。
  • Zookeeper服務(wù)器有三種角色:Leader、Follower、Observer,集群中的所有機器通過一個 Leader 選舉過程來選定一臺稱為 “Leader” 的機器。Leader 既可以為客戶端提供寫服務(wù)又能提供讀服務(wù)。除了 Leader 外,FollowerObserver只能提供讀服務(wù)FollowerObserver 唯一的區(qū)別在于Observer 機器不參與 Leader 的選舉過程,也不參與寫操作的“過半寫成功”策略,因此 Observer 機器可以在不影響寫性能的情況下提升集群的讀性能。
  • ZooKeeper 中,主要依賴 ZAB 協(xié)議來實現(xiàn)分布式數(shù)據(jù)一致性,基于該協(xié)議,ZooKeeper實現(xiàn)了一種主備模式的系統(tǒng)架構(gòu)來保持集群中各個副本之間的數(shù)據(jù)一致性

工作原理

  1. Zookeeper的核心是原子廣播,這個機制保證了各個server之間的同步。實現(xiàn)這個機制的協(xié)議叫做Zab協(xié)議。
  2. Zab協(xié)議有兩種模式,它們分別是恢復(fù)模式廣播模式。當(dāng)服務(wù)啟動或者在領(lǐng)導(dǎo)者崩潰后,Zab就進(jìn)入了恢復(fù)模式,當(dāng)領(lǐng)導(dǎo)者被選舉出來,且大多數(shù)server的完成了和leader狀態(tài)同步以后,恢復(fù)模式就結(jié)束了。狀態(tài)同步保證了leaderserver具有相同的系統(tǒng)狀態(tài)。一旦leader已經(jīng)和多數(shù)的follower進(jìn)行了狀態(tài)同步后,他就可以開始廣播消息了,即進(jìn)入廣播狀態(tài)。這時候當(dāng)一個server加入zookeeper服務(wù)中,它會在恢復(fù)模式下啟動,發(fā)現(xiàn)leader,并和leader進(jìn)行狀態(tài)同步。待到同步結(jié)束,它也參與消息廣播。
  3. Zookeeper服務(wù)一直維持在Broadcast狀態(tài),直到leader崩潰了或者leader失去了大部分的followers支持。

Leader選舉

  1. 廣播模式需要保證proposal提議)被按順序處理leader來執(zhí)行寫操作),因此zk采用了遞增的事務(wù)id號(zxid)來保證。所有的提議都在被提出的時候加上了zxid。實現(xiàn)中zxid是一個64為的數(shù)字,它高32位是epoch用來標(biāo)識leader關(guān)系是否改變,每次一個leader被選出來,它都會有一個新的epoch。低32位是個遞增計數(shù)。
  2. 當(dāng)leader崩潰或者leader失去大多數(shù)的follower,這時候zk進(jìn)入恢復(fù)模式,恢復(fù)模式需要重新選舉出一個新的leader,讓所有的server恢復(fù)到一個正確的狀態(tài)。每個Server啟動以后都詢問其它的Server它要投票給誰。對于其他server的詢問,server每次根據(jù)自己的狀態(tài)都回復(fù)自己推薦的leaderid和上一次處理事務(wù)的zxid(系統(tǒng)啟動時每個server都會推薦自己),收到所有Server回復(fù)以后,就計算出zxid最大的哪個Server,并將這個Server相關(guān)信息設(shè)置成下一次要投票的Server。計算這過程中獲得票數(shù)最多的的sever為獲勝者,如果獲勝者的票數(shù)超過半數(shù),則改server被選為leader。否則,繼續(xù)這個過程,直到leader被選舉出來。

二.使用Zookeeper

 1        //客戶端連接zookeeper服務(wù)器
 2         ZooKeeper zkClient = new ZooKeeper(CONNECT_STR, 50000, new Watcher() {
 3             @Override
 4             public void process(WatchedEvent watchedEvent) {
 5                 //監(jiān)控服務(wù)節(jié)點變化
 6                 System.out.println("sssss");
 7             }
 8         });
 9         
10         //獲取根節(jié)點下的所有節(jié)點
11         List<String> nodeList= zkClient.getChildren("/",null);
12 
13         System.out.println(nodeList.toString());
14 
15         //Stat isExists= zkClient.exists(LOCK_ROOT_PATH,null);
16         //在test父節(jié)點下創(chuàng)建子節(jié)點
17         String lockPath = zkClient.create("/test/why","why".getBytes(),            
18         ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);

代碼中需要注意的是如果父節(jié)點不存在,會報異常,同時父節(jié)點不能是臨時節(jié)點。

Znode

  1. ZooKeeper中,“節(jié)點"分為兩類,第一類同樣是指構(gòu)成集群的機器,我們稱之為機器節(jié)點,第二類則是指數(shù)據(jù)模型中的數(shù)據(jù)單元,我們稱之為數(shù)據(jù)節(jié)點一ZNode。
  2. ZooKeeper 將所有數(shù)據(jù)存儲在內(nèi)存中,數(shù)據(jù)模型是一棵樹(Znode Tree),由斜杠(/)的進(jìn)行分割的路徑,就是一個
    Znode,例如/foo/path2。每個上都會保存自己的數(shù)據(jù)內(nèi)容,同時還會保存一系列屬性信息。
  3. zookeeper有四類節(jié)點:PERSISTENT(持久的)、EPHEMERAL(暫時的)、PERSISTENT_SEQUENTIAL(持久化順序編號目錄節(jié)點)EPHEMERAL_SEQUENTIAL(暫時化順序編號目錄節(jié)點)

Session

  1. Session 指的是 ZooKeeper 服務(wù)器與客戶端會話。在 ZooKeeper 中,一個客戶端連接是指客戶端和服務(wù)器之間的一個 TCP 長連接。
  2. 客戶端啟動的時候,首先會與服務(wù)器建立一個 TCP連接,從第一次連接建立開始,客戶端會話的生命周期也開始了。通過這個連接,客戶端能夠通過心跳檢測與服務(wù)器保持有效的會話,也能夠向Zookeeper 服務(wù)器發(fā)送請求并接受響應(yīng),同時還能夠通過該連接接收來自服務(wù)器的 Watch 事件通知。
  3. SessionsessionTimeout值用來設(shè)置一個客戶端會話的超時時間。當(dāng)由于服務(wù)器壓力太大、網(wǎng)絡(luò)故障或是客戶端主動斷開連接等各種原因?qū)е驴蛻舳诉B接斷開時,只要在sessionTimeout規(guī)定的時間內(nèi)能夠重新連接上集群中任意一臺服務(wù)器,那么之前創(chuàng)建的會話仍然有效。在為客戶端創(chuàng)建會話之前,服務(wù)端首先會為每個客戶端都分配一個sessionID。由于 sessionIDZookeeper 會話的一個重要標(biāo)識,許多與會話相關(guān)的運行機制都是基于這個sessionID 的。因此,無論是哪臺服務(wù)器為客戶端分配的 sessionID,都務(wù)必保證全局唯一。

Watcher:是 ZooKeeper 中的一個很重要的特性。ZooKeeper 允許用戶在指定節(jié)點上注冊一些 Watcher,并且在一些特定事件觸發(fā)的時候,ZooKeeper 服務(wù)端會將事件通知到感興趣的客戶端上去,該機制是 ZooKeeper 實現(xiàn)分布式協(xié)調(diào)服務(wù)的重要特性。

Version: Zookeeper 的每個 ZNode 上都會存儲數(shù)據(jù),對應(yīng)于每個 ZNode,Zookeeper 都會為其維護一個叫作 Stat 的數(shù)據(jù)結(jié)構(gòu)。Stat 中記錄了這個 ZNode 的三個數(shù)據(jù)版本,分別是:version(當(dāng)前節(jié)點版本)、cversion當(dāng)前節(jié)點的子節(jié)點版本)、aversion當(dāng)前節(jié)點的ACL版本

ACLZooKeeper 采用 ACLAccessControlLists)策略來進(jìn)行權(quán)限控制,類似于 UNIX 文件系統(tǒng)的權(quán)限控制。ZooKeeper 定義了 5 種權(quán)限:CREATE/READ/WRITE/DELETE/ADMIN


三.通過zookeeper實現(xiàn)分布式鎖

      package com.why;

      import org.apache.zookeeper.*;
      import org.apache.zookeeper.data.Stat;

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

     /*
     *  分布式鎖
     * */
     public class DistributeLock {

         private static final String LOCK_ROOT_PATH = "/test";
         //private static final String LOCK_NODE_NAME = "Lock";

         private static ZooKeeper _zkClient;

         static {
             try {
                 _zkClient = new ZooKeeper("192.168.6.132:2181", 500000, null);
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }

         public static String getLock() {
             try {

                 //System.out.println(_zkClient.getChildren("/",false));

                 String lockPath = _zkClient.create( "/test/why", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
                 //System.out.println(lockPath);
                 //System.out.println(_zkClient.getChildren(LOCK_ROOT_PATH,false));
                 if (tryLock(lockPath))
                     return lockPath;
                 else
                     return null;
             } catch (Exception ex) {
                 ex.printStackTrace();
                 return null;
             }
         }

         private static boolean tryLock(String lockPath) throws KeeperException, InterruptedException {
             List<String> lockPaths = _zkClient.getChildren(LOCK_ROOT_PATH, false);
             Collections.sort(lockPaths);
             int index=lockPaths.indexOf(lockPath.substring(LOCK_ROOT_PATH.length()+1));
             if(index==0){
                 //獲得鎖
                 return true;
             }
             else{
                 String preLockPath="/"+lockPaths.get(index-1);

                 Watcher watcher=new Watcher() {
                     @Override
                     public void process(WatchedEvent watchedEvent) {
                         synchronized (this){
                             //喚醒線程
                             notifyAll();
                         }
                     }
                 };

                 Stat stat=_zkClient.exists(preLockPath,watcher);

                 if(stat==null){
                     return tryLock(lockPath);
                 }else{
                     synchronized (watcher){
                         watcher.wait();
                     }
                     return tryLock(lockPath);
                 }

             }

         }

         public static void closeZkClient() throws InterruptedException {
             _zkClient.close();
         }

         public static void releaseLock(String lockPath) throws KeeperException, InterruptedException {
             _zkClient.delete(lockPath,-1);
         }
     }

測試:

    package com.why;

    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.ZooKeeper;

    import java.io.IOException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class MultiThreadDemo {

        private static  int counter = 0;

        public  static  void plus() throws InterruptedException {
            Thread.sleep(500);
            counter++;
            //System.out.println(counter);
        }

        public static int Count(){
            return counter;
        }

        public static void main(String[] args) throws IOException, KeeperException, InterruptedException {

            ExecutorService executor= Executors.newCachedThreadPool();
            final int num=10;
            for(int i=0;i<num;i++){
                executor.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            String path = DistributeLock.getLock();
                            System.out.println(path);
                            plus();
                            DistributeLock.releaseLock(path);
                            System.out.println(Count());
                        } catch (InterruptedException | KeeperException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
            executor.shutdown();

        }
    }

文末彩蛋

針對于上面所涉及到的知識點我總結(jié)出了有1到5年開發(fā)經(jīng)驗的程序員在面試中涉及到的絕大部分架構(gòu)面試題及答案做成了文檔和架構(gòu)視頻資料免費分享給大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并發(fā)等架構(gòu)技術(shù)資料),希望能幫助到您面試前的復(fù)習(xí)且找到一個好的工作,也節(jié)省大家在網(wǎng)上搜索資料的時間來學(xué)習(xí),也可以關(guān)注我一下以后會有更多干貨分享。

資料獲取方式 QQ群搜索“708-701-457” 即可免費領(lǐng)取

zookeeper入門到實戰(zhàn)
zookeeper入門到實戰(zhàn)
zookeeper入門到實戰(zhàn)

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI