BigData~02:Hadoop基礎01 ~ ZooKeeper

...

一、相關概念

  1. 中間件:為分布式系統提供協調服務的組件,如專門用于計算服務的機器就是一個計算型中間件,還有專門用于存儲的機器就是存儲中間件等等;
  2. 分布式系統:把一套系統按照業務進行橫向拆分,不同業務階段的處理放在不同的計算機上,由這些計算機組成了一套業務處理鏈,那么這個業務處理鏈就是一個分布式系統;
    • 內部的每臺計算機都可以進行通信(rpc/rest);
    • 可以這樣理解:加入我們的系統目前就在一個項目中,橫向拆分就是把MVC層拆出來作為一個模塊節點A,把Service層拆分出來當做節點B,把DAO層拆分出來當做節點C,那么節點A可以部署在服務器a上,節點B部署在服務器b上,節點C部署在節點c上;運行的時候客戶端的請求發送到服務器a上,a處理的時候使用rpc/rest調用b,b處理的時候調用c,這就是所謂的分布式系統;而且這個系統的特點很明顯,如果b的處理壓力過大,那么b節點可以單獨的擴展為集群,這樣這個系統就成為了一個分布式集群系統,大大增加了系統的可擴展性,以及系統運行時候的性能的保障;
  3. ZooKeeper:一個基于觀察者模式設計的,為用戶的分布式應用程序提供分布式的協調服務的中間件框架;簡稱ZK;主要用來解決分布式集群中應用系統的一致性問題;支持Java,并且提供java和c的相關API;
  4. ZK的作用
    • Leader節點選舉:就是一個主備切換的過程,當主節點掛了以后,從節點就可以接手主節點的工作,保證系統的高可用。
    • 統一配置管理:只需要配置部署一臺服務器,就可以把這個服務器上的配置資源更新到其他的服務器上。常用于云計算部署;
    • 發布訂閱: 類似于MQ、Dubbo,發布者把數據發布到znode上,訂閱者會去獲取這個信息;
    • 提供分布式鎖:在分布式環境中,各個服務器可能會搶奪同一個資源,分布式鎖就是解決這個搶奪過程的同步問題;
    • 集群管理:集群中各個服務器之間保持數據的強一致性;
      • ZK中的節點角色分兩種:Leader和Follower/Observer,只有集群中有半數以上節點存活,集群就能提供服務,一般情況下集群的節點數目不少于3個且為奇數個;一個Leader,多個Follower組成一個ZK集群;

二、ZooKeeper單機安裝配置

  1. 下載安裝
    • 建議在官方歸檔頁面下載;
    • 解壓下載到的安裝包,配置環境變量ZOOKEEPER_HOMEPATH
  2. ZooKeeper的配置
    • 安裝完成后,在conf文件夾下面有一個文件:zoo_sample.cfg文件,拷貝這個文件為zoo.cfg;
    • 在這個配置文件中有很多的常用的變量需要我們配置:
      • tickTime:這個時間是作為 Zookeeper 服務器之間或客戶端與服務器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳,單位:毫秒,單機集群通用配置;
      • initLimit:初始化時,Follower節點連接到Leader節點的最長能忍受的心跳間隔數,按照tickTime倍數形式表示,集群配置;
      • syncLimit:Follower節點和Leader節點之間發送請求與應答最多能忍受的心跳間隔,集群配置;
      • dataDir:常用數據所在目錄,必須配置,單機集群通用配置;
      • dataLogDir:日志目錄,如果沒有配置,則使用dataDir,單機集群通用配置;
      • clientPort:連接服務器的端口,默認2181,單機集群通用配置;
  3. ZooKeeper的使用
    • 啟動ZooKeeper服務:
      ./zkServer.sh start
      ./zkServer.sh start-foreground
      
    • 關閉ZooKeeper服務:
      ./zkServer.sh stop
      
    • 重啟ZooKeeper服務:
      ./zkServer.sh restart
      
    • 查看ZooKeeper服務狀態:
      ./zkServer.sh status
      

三、ZooKeeper的數據模型

  1. ZK的數據模型
    • 是一個樹形模型;
    • 每一個節點成為znode,每個節點都存儲自身的數據,可以有子節點;
    • 每個節點都有兩種類型:臨時和永久;臨時節點在與客戶端斷開連接之后消失,子節點和自身數據都會丟失;
    • 每個節點都有一個版本號,該版本號會隨著該節點的信息更新而更新(類似數據庫的樂觀鎖),這個版本信息可以通過命令來展示;
    • 每個節點存儲的數據大小不宜過大(幾K);
    • 每個節點都可以設置ACL權限;
  2. ZK的數據模型的相關操作
    • 連接客戶端,必須先啟動Server,再去使用Client連接Server
      ./zkCli.sh
      
    • zkCli相關命令
      • 查看zkCli的所有命令
        help
        
      • 列舉當前節點的子節點
        ls <node-path> [watch]
        
      • 查看當前節點的狀態
        stat <node-path>
        
        • cZxid:節點id;
        • cTime:create time;
        • mZXid:修改節點后的節點id;
        • mTime:modify time;
        • pZxid:子節點的id???
        • cversion:子節點的version;
        • dataVersion:當前節點的數據版本;
        • aclVersion:acl權限版本;
        • ephemeralOwner:臨時Owner內存地址,如果值是0x0,那么這個節點就是持久性節點,否則這個節點就是臨時節點;臨時節點不是一斷開就立即刪除(心跳機制,斷開連接之后,沒有了心跳,就會去刪除臨時節點),而是有一個實效時間,在這個時間過后才會刪除;
        • dataLength:數據字節數;
        • numChildren:子節點個數;
      • 同事查看當前節點的子節點以及當前節點的狀態
        ls2 <node-path> [watch] # 本質上就是ls和stat組合命令
        
      • 查看當前節點的數據
        get <node-path>
        
    • 關閉客戶端連接
      • Ctrl + C

四、ZK的常用操作

  1. Session基本原理
    • 服務端和客戶端之間的連接稱之為Session;
    • 每個Session都可以設置一個超市時間;
    • 心跳結束,Session過期,臨時節點就會被丟棄;
    • 心跳機制:客戶端向服務端的ping包請求;
  2. 常用命令
    • 創建節點
      create [-s] [-e] <node-path> <node-data> <acl>
      
      • -s:sequence,創建順序節點
      • -e:ephemeral,創建臨時節點;臨時節點上不可以創建永久節點;
    • 修改節點
      set <node-path> <node-data> [<version>]
      
    • 刪除節點
      delete <node-path> [<version>]
      

五、watch機制

  1. 機制簡介
    • 我們對每個節點的操作都會有一個監聽者watch,可以理解為一個觸發器;當有人操作了節點,那么就會觸發這個節點的watch事件,來更新這個節點以及父節點的屬性值;
    • watch是一次性的,觸發后事件完成后就立即銷毀;可以通過Apache實現永久性的watch;
    • 不同的類型的watch觸發的事件也是不同的:
      • 節點創建
      • 節點刪除
      • 節點數據更新
    • 常見的watch事件
      • 當前節點創建節點觸發事件:NodeCreated
        • 如果當前節點不存在也可以設置,報錯可忽略
      • 當前節點數據更新事件:NodeDataChanged
      • 當前節點刪除事件:NodeDeleted
      • 子節點創建、刪除事件:NodeChildrenChanged
      • 修改子節點,不觸發父節點事件
  2. watch相關的命令
    • 通過查詢數據設置watch
      get <node-path> [watch]
      
    • 通過查詢節點狀態設置watch
      stat <node-path> [watch]
      
    • 通過列舉子節點設置watch
      ls[2] <node-path> [watch]
      
  3. Watch使用場景
    • 集群統一資源配置

六、權限控制列表

  1. ACL簡介
    • 權限控制列表:ACL(Access Control List)
    • 為了保障數據的安全性,針對節點設置相關的讀寫權限;這個權限可以設置不同的權限范圍和角色;
    • ACL的結構單位:[<scheme>:<id>:<permissions>]
      • scheme:采用的權限機制
        1. world:這個scheme下只有一個id,即anyone;組合world:anyone:<permissions>
        2. auth:認證登錄;組合:auth:<username>:<password>:<permissions>
        3. digest:加密訪問;組合digest:<username>:BASE64(SHA1(<password>)):<persissions>
        4. ip:限制ip訪問;組合ip:<ip-addr>:<permissions>
        5. super:超級管理員權限;
          • 編輯zkServer.sh中,找到行nohup行的${ZOO_LOG4J_PROP}",在其后添加"-Dzookeeper.DigestAuthenticationProvider.superDigest=<username>:<BASE64(SHA1(<password>))>"
          • 重啟zkServer;
          • 登陸認證:addauth digest <username>:<BASE64(SHA1(<password>))>,這樣就可以使用超級管理員賬號了;
      • id:允許訪問的用戶id
      • permissions:權限字符串:CRDWA,這5個字母可以任意組合
        1. Create:創建權限
        2. Read:讀當前節點和子節點的權限
        3. Delete:刪除權限
        4. Write:寫權限
        5. Admin:分配權限的權限
  2. ACL使用
    • addauth:添加認證授權信息:
      addauth <scheme> <acl>
      # 添加一個用戶名為zhangsan,密碼為123456的用戶:認證過程
      addauth digest zhangsan:123456
      
    • setAcl:設置具體節點的權限信息:
      setAcl <node-path> <acl>
      # 給一個用戶zhangsan設置權限,注意:只是第一次設置有效
      setAcl /level1/level2 auth:zhangsan:123456:cdrwa
      
    • getAcl:獲取具體節點的權限信息:
      getAcl <node-path>
      # 默認權限:
      'world,' anyone
      : cdrwa
      
  3. ACL使用場景
    • 分離開發測試環境:即不同的節點設置不同的訪問權限;

七、四字命令(The Four Letter Words)

  1. 簡介
    • ZK可以通過四字命令與服務器進行交互;比較適合監控信息;
  2. 使用
    • 安裝nc:yum install nc
    • nc命令使用規則:echo [command] | nc [ip] [port]
  3. 常用命令
    • stat:查看ZK的狀態信息;
      echo stat | nc localhost 2181
      # 執行結果:
      Zookeeper version: 3.4.9-1757313, built on 08/23/2016 06:50 GMT
      Clients:
      /localhost:46026[0](queued=0,recved=1,sent=0)
      Latency min/avg/max: 0/1/8
      Received: 10
      Sent: 9
      Connections: 1
      Outstanding: 0
      Zxid: 0xa
      Mode: standalone
      Node count: 4
      
    • ruok:查看ZK的啟動狀態;
      echo ruok | nc localhost 2181
      # 執行結果:
      imok
      
    • dump:查看ZK的啟動狀態;
      echo dump | nc localhost 2181
      # 執行結果:
      SessionTracker dump:
      Session Sets (0):
      ephemeral nodes dump:
      Sessions with Ephemerals (0):
      
    • conf:查看ZK服務器的相關配置參數;
      echo conf | nc localhost 2181
      # 執行結果:
      clientPort=2181
      dataDir=/usr/local/bin/zookeeper-3.4.9/data/version-2
      dataLogDir=/usr/local/bin/zookeeper-3.4.9/log/version-2
      tickTime=2000
      maxClientCnxns=60
      minSessionTimeout=4000
      maxSessionTimeout=40000
      serverId=0
      
    • cons:查看連接到ZK服務器的信息;
      echo cons | nc localhost 2181
      # 執行結果:
       /0:0:0:0:0:0:0:1:49998[0](queued=0,recved=1,sent=0)
       /0:0:0:0:0:0:0:1:49996[1](queued=0,recved=1,sent=1,sid=0x1645e34b63f0001,lop=SESS,est=1530597540388,to=30000,lcxid=0x0,lzxid=0xb,lresp=1530597540403,llat=14,minlat=0,avglat=14,maxlat=14)
      
    • envi:查看ZK服務器的環境變量;
      echo envi | nc localhost 2181
      # 執行結果:
      Environment:
      zookeeper.version=3.4.9-1757313, built on 08/23/2016 06:50 GMT
      host.name=localhost
      java.version=1.8.0_171
      java.vendor=Oracle Corporation
      java.home=/usr/local/bin/jdk1.8.0_171/jre
      java.class.path=/usr/local/bin/zookeeper-3.4.9/bin/../build/classes:/usr/local/bin/zookeeper-3.4.9/bin/../build/lib/*.jar:/usr/local/bin/zookeeper-3.4.9/bin/../lib/slf4j-log4j12-1.6.1.jar:/usr/local/bin/zookeeper-3.4.9/bin/../lib/slf4j-api-1.6.1.jar:/usr/local/bin/zookeeper-3.4.9/bin/../lib/netty-3.10.5.Final.jar:/usr/local/bin/zookeeper-3.4.9/bin/../lib/log4j-1.2.16.jar:/usr/local/bin/zookeeper-3.4.9/bin/../lib/jline-0.9.94.jar:/usr/local/bin/zookeeper-3.4.9/bin/../zookeeper-3.4.9.jar:/usr/local/bin/zookeeper-3.4.9/bin/../src/java/lib/*.jar:/usr/local/bin/zookeeper-3.4.9/bin/../conf:
      java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
      java.io.tmpdir=/tmp
      java.compiler=<NA>
      os.name=Linux
      os.arch=amd64
      os.version=3.10.0-862.3.2.el7.x86_64
      user.name=root
      user.home=/root
      user.dir=/usr/local/bin/zookeeper-3.4.9/bin
      
    • mntr:查看ZK服務器的健康信息;
      echo mntr | nc localhost 2181
      # 執行結果:
      zk_version      3.4.9-1757313, built on 08/23/2016 06:50 GMT
      zk_avg_latency  1
      zk_max_latency  14
      zk_min_latency  0
      zk_packets_received     59
      zk_packets_sent 58
      zk_num_alive_connections        2
      zk_outstanding_requests 0
      zk_server_state standalone
      zk_znode_count  4
      zk_watch_count  0
      zk_ephemerals_count     0
      zk_approximate_data_size        27
      zk_open_file_descriptor_count   27
      zk_max_file_descriptor_count    4096
      
    • wchs:查看ZK服務器的watch信息;
      echo wchs | nc localhost 2181
      # 執行結果:
      0 connections watching 0 paths
      Total watches:0
      
    • wchc:查看ZK服務器中session與帶有watch的節點的關系信息(3.4.10后需要開啟白名單才能執行);
      echo wchc | nc localhost 2181
      
    • wchp:查看ZK服務器中帶有watch的節點在哪個session中(3.4.10后需要開啟白名單才能執行);
      echo wchp | nc localhost 2181
      
    • 開啟白名單:在zoo.cfg文件中添加如下一行命令,重啟即可:
      4lw.commands.whitelist=*
      4lw.commands.whitelist=<cmd1>,<cmd2>...
      

八、ZK搭建集群

  1. 偽分布式環境搭建(為學習)

    • 下載ZooKeeper安裝包;
    • 解壓安裝包;
    • 復制conf文件夾下的zoo_sample.cfg為zoo.cfg;
    • 配置各個節點的myid:向每個節點的ZK的數據文件夾中寫入一個文件myid,這個文件中寫入當前機器的id編號即可;
    • 編輯zoo.cfg:
      • 配置dataDirdataLogDir,不要放置在tmp下,個人喜歡放在安裝目錄或者獨立的硬盤的一個目錄下,并且確保程序有訪問權限;
      • 在每個節點上配置所有節點的端口映射關系:①對外訪問端口:2181;②Leader和Follower之間的端口:2888;③組件之間投票通訊端口:3888;對應的配置如下:
        <server-name-prefix>.<myid>=<host>:<leader-listener-port>:<vote-port>
        
        1. <leader-listener-port>:是該服務器一旦成為Leader之后需要監聽的端口,用于接收來自follower的請求;
        2. <vote-port>:集群中的每一個節點在最開始選舉Leader時監聽的端口,用于服務器互相之間通信選舉Leader;
    • 建議關閉防火墻,因為集群只在內部訪問,不會暴露到對外的環境中;
  2. 真分布式環境搭建(為應用)

九、ZK集群的特點

  1. 數據一致性:每個ZK節點保存一份相同的數據副本,client無論連接到哪個節點,數據都是一致的,即單一視圖
  2. 分布式讀寫,更新請求轉發,由leader實施;更新請求順序進行,來自同一個client的更新請求按其發送順序依次執行;
  3. 操作原子性:一損俱損,全榮為榮;
  4. 數據實時性:在一定時間范圍內,client能讀到最新數據,ZK節點越多實時性越差;
  5. 數據可靠性:每次對ZK的操作都記錄在服務端;

十、ZK選舉Leader

  1. 問題的提出:
    • 在配置集群的時候我么是不配置哪個節點是Leader節點,哪些事Follower節點的,當我們在每個節點上配置了這個集群的所有節點的映射關系之后,當ZK節點一個接著一個的啟動,系統會自動的選舉出Leader,那這個Leader的節點選舉時怎樣進行的呢?
  2. Leader選舉機制
    • Leader選舉機制的考慮因素:
      • 節點id:id即權重,節點id越大選舉的權重越大;
      • 數據id:數據越新,數據id越大,選舉的權重越大;
      • 投票次數:得票者越大,越容易成為Leader;
    • 選舉中節點的狀態:
      • LOOKING:競選狀態;
      • FOLLOWING:隨從狀態,同步leader狀態,參與投票;
      • OBSERVING:觀察狀態,同步leader狀態,不參與投票;
      • LEADING:領導者狀態;

十一、ZK的JavaAPI的使用

  1. 添加ZooKeeper的pom:
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.5</version>
    </dependency>
    
    • org.apache.zookeeper.Zookeeper是客戶端入口主類,負責建立與ZK服務器節點的會話;主要的API參考下面的測試類;
  2. ZKClientTest:
    import lombok.extern.slf4j.Slf4j;
    import org.apache.zookeeper.*;
    import org.apache.zookeeper.data.Stat;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.util.List;
    
    /**
     * @Author: ShrekerNil
     * @Date: 2018/7/5 10:28
     * @Description: ZooKeeper的API的使用
     */
    @Slf4j
    public class ZKClientTest {
    
        private static final String connectString = "192.168.70.131:2181,192.168.70.132:2181,192.168.70.133:2181";
        private static final int sessionTimeout = 2000;
    
        private ZooKeeper zkClient = null;
    
        @Before
        public void init() throws Exception {
            zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
                // Watcher接口是當zkClient監聽到watch事件觸發的時候就會去調用該接口process方法的一個接口
                public void process(WatchedEvent event) {
                    // 收到事件通知后的回調函數(應該是我們自己的事件處理邏輯)
                    log.info("接收到事件,類型:{},事件發生節點:{}", event.getType(), event.getPath());
                    // 因為監聽器只會生效一次,所以在消費監聽器之后再次注冊監聽器
                    try {
                        zkClient.getChildren("/", true);
                    } catch (Exception e) {
                        log.error("添加監聽器失敗", e);
                    }
                }
            });
    
        }
    
        // 創建數據節點到ZK中
        @Test
        public void testCreate() throws Exception {
            // 參數1:要創建的節點的路徑 參數2:節點大數據 參數3:節點的權限 參數4:節點的類型
            String nodePathCreated = zkClient.create("/node110", "hellozk".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            //上傳的數據可以是任何類型,但都要轉成byte[]
            log.info(nodePathCreated);
        }
    
        //判斷znode是否存在
        @Test
        public void testExist() throws Exception {
            Stat stat = zkClient.exists("/node110", false);
            log.info(stat == null ? "Not exist!" : "Exists");
        }
    
        // 獲取子節點
        @Test
        public void getChildren() throws Exception {
            List<String> children = zkClient.getChildren("/", true);
            for (String child : children) {
                log.info(child);
            }
            Thread.sleep(20000);
        }
    
        //獲取znode的數據
        @Test
        public void getNodeData() throws Exception {
            byte[] data = zkClient.getData("/node110", false, null);
            log.info(new String(data));
        }
    
        //設置znode數據
        @Test
        public void setNodeData() throws Exception {
            zkClient.setData("/node110", "something in my heart is ...".getBytes(), -1);
            byte[] data = zkClient.getData("/node110", false, null);
            log.info(new String(data));
        }
    
        //刪除znode
        @Test
        public void deleteZnode() throws Exception {
            //第二個參數:指定要刪除的版本,-1表示刪除所有版本
            zkClient.delete("/node110", -1);
        }
    
    }
    

十二、ZK的使用場景(參考自這里)

  1. 統一命名服務(Name Service)
    • 統一命名服務是ZK的內置功能,類似于JNDI,我們在調用其API的時候使用了這一功能,在這里不再贅述;
  2. 配置管理(Configuration Management)
    • 問題:配置的管理在分布式應用環境中很常見,例如同一個應用系統需要多臺服務器配合運行,但是它們運行的應用系統的某些配置項是相同的,如果要修改這些相同的配置項,那么就必須同時修改每臺運行這個應用系統的服務器,這樣非常麻煩而且容易出錯。
    • 解決:ZK就能很好的幫我們完成這項配置工作,可以把配置存儲在ZK的一個節點上,設置監聽,當我們修改了ZK中這個節點的數據的時候,監聽器就能偵測到這個事件,進而把配置應用到各自的服務器中。
  3. 集群管理(Group Membership)
    • 問題:在做集群的時候,一般我們都使用Master、Slave模式,但是如果Master掛了呢?這個時候就會出現問題,整個服務就掛掉了,HA化為泡影;
    • 解決:這個問題的解決方式有很多,很多的框架都提供了集群管理的功能,當然我們今天的主角ZK也不例外。首先我們應該注冊監聽一個節點的子節點,客戶端在服務器上注冊臨時(EPHEMERAL)的節點,當出現如上的問題的時候,就觸發ZK的事件,我們就知道哪個節點掛了,根據一定的算法選舉出新的Leader,讓集群接著向外界提供服務;
  4. 共享鎖(Locks)
    • 共享鎖在同一個進程中很容易實現,但是在跨進程或者在不同 Server 之間就不好實現了。Zookeeper 卻很容易實現這個功能,實現方式也是需要獲得鎖的 Server 創建一個 EPHEMERAL_SEQUENTIAL 目錄節點,然后調用 getChildren方法獲取當前的目錄節點列表中最小的目錄節點是不是就是自己創建的目錄節點,如果正是自己創建的,那么它就獲得了這個鎖,如果不是那么它就調用 exists(String path, boolean watch) 方法并監控 Zookeeper 上目錄節點列表的變化,一直到自己創建的節點是列表中最小編號的目錄節點,從而獲得鎖,釋放鎖很簡單,只要刪除前面它自己所創建的目錄節點就行了。
  5. 隊列管理
    • ZK可以處理兩種類型的隊列:同步隊列和FIFO隊列(完整代碼參看這里);
    • 同步隊列實現:當一個隊列的成員都聚齊時,這個隊列才可用,否則一直等待所有成員到達;
      • 實現思路:創建一個父目錄/synchronizing,每個成員都監控標志(Set Watch)位目錄 /synchronizing/start是否存在,然后每個成員都加入這個隊列,加入隊列的方式就是創建 /synchronizing/member_i的臨時目錄節點,然后每個成員獲取/ synchronizing目錄的所有目錄節點,也就是member_i。判斷i的值是否已經是成員的個數,如果小于成員個數等待/synchronizing/start的出現,如果已經相等就創建/synchronizing/start
    • FIFO隊列實現:隊列按照 FIFO 方式進行入隊和出隊操作,例如實現生產者和消費者模型;
      • 實現思路:實現的思路也非常簡單,就是在特定的目錄下創建SEQUENTIAL類型的子目錄 /queue_i,這樣就能保證所有成員加入隊列時都是有編號的,出隊列時通過getChildren()方法可以返回當前所有的隊列中的元素,然后消費其中最小的一個,這樣就能保證 FIFO;

十三、集群節點上下線感知系統實現

  1. 客戶端實現

    import lombok.extern.slf4j.Slf4j;
    import org.apache.zookeeper.WatchedEvent;
    import org.apache.zookeeper.Watcher;
    import org.apache.zookeeper.ZooKeeper;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Author: ShrekerNil
     * @Date: 2018/7/5 14:21
     * @Description: DistributedClient用來描述ZK在客戶端的用法
     */
    @Slf4j
    public class DistributedClient {
    
        private static final String connectString = "192.168.70.131:2181,192.168.70.132:2181,192.168.70.133:2181";
        private static final int sessionTimeout = 2000;
        private static final String baseNode = "/servers";
        private ZooKeeper zkClient = null;
        private volatile List<String> servers;
    
        /**
         * 創建到zk的客戶端連接
         */
        private void connectZoooKepper() throws Exception {
            zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
                public void process(WatchedEvent event) {
                    try {
                        updateServersAndListenChildrenNodes();
                    } catch (Exception e) {
                        log.error("更新服務器列表失敗", e);
                    }
                }
            });
        }
    
        /**
         * 更新服務器列表,并且注冊子節點監聽
         */
        private void updateServersAndListenChildrenNodes() throws Exception {
            // 獲取服務器子節點信息,并且對父節點進行監聽
            List<String> nodes = zkClient.getChildren(baseNode, true);
    
            // 先創建一個局部的list來存服務器信息
            List<String> temp = new ArrayList<String>();
            for (String node : nodes) {
                // child只是子節點的節點名
                byte[] data = zkClient.getData(baseNode + "/" + node, false, null);
                temp.add(new String(data));
            }
            servers = temp;
    
            //打印服務器列表
            log.debug("servers:{}", servers.toString());
        }
    
        /**
         * 業務功能
         */
        private void handleBussiness() throws InterruptedException {
            log.debug("Client start working.....");
            log.debug("Using servers:{}", servers);
            Thread.sleep(Long.MAX_VALUE);
        }
    
        public static void main(String[] args) throws Exception {
            // 創建客戶端對象
            DistributedClient client = new DistributedClient();
            // 連接ZK
            client.connectZoooKepper();
            // 更新servers的子節點信息(并監聽),從中獲取服務器信息列表
            client.updateServersAndListenChildrenNodes();
            // 業務線程啟動
            client.handleBussiness();
        }
    
    }
    
  2. 集群端實現:

    import lombok.extern.slf4j.Slf4j;
    import org.apache.zookeeper.*;
    
    /**
     * @Author: ShrekerNil
     * @Date: 2018/7/5 14:19
     * @Description: DistributedServer用來描述ZK在服務器端的用法
     */
    @Slf4j
    public class DistributedServer {
    
        private static final String connectString = "192.168.70.131:2181,192.168.70.132:2181,192.168.70.133:2181";
        private static final int sessionTimeout = 2000;
        private static final String parentNode = "/servers";
    
        private ZooKeeper zkClient = null;
    
        /**
         * 創建到zk的客戶端連接
         */
        private void connectZoooKepper() throws Exception {
            zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
                public void process(WatchedEvent event) {
                    // 收到事件通知后的回調函數(應該是我們自己的事件處理邏輯)
                    log.debug(event.getType() + "---" + event.getPath());
                    try {
                        zkClient.getChildren("/", true);
                    } catch (Exception e) {
                        log.error("添加監聽器失敗", e);
                    }
                }
            });
        }
    
        /**
         * 向zk集群注冊服務器信息
         */
        private void registerServer(String hostname) throws Exception {
            String newNode = zkClient.create(parentNode + "/server", hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            log.debug("Created new node {} in {}", newNode, hostname);
        }
    
        /**
         * 業務功能
         */
        private void handleBussiness(String hostname) throws InterruptedException {
            log.debug(hostname + " start working.....");
            Thread.sleep(Long.MAX_VALUE);
        }
    
        public static void main(String[] args) throws Exception {
            // 創建Server
            DistributedServer server = new DistributedServer();
    
            // 連接ZK
            server.connectZoooKepper();
    
            int length = args.length;
            if (length < 1) {
                log.error("請添加參數hostname");
                return;
            }
    
            // 利用zk連接注冊服務器信息
            server.registerServer(args[0]);
    
            // 啟動業務功能
            server.handleBussiness(args[0]);
        }
    
    }
    

十四、寫在最后

  1. 在本篇文章中提到了很多知識點,但是很多知識點只是提了一下,由于篇幅有限,只能寫到這里了,如果需要再補充吧
  2. 補充一個原理講解比較深刻的博客,大家可以去看看;
  3. 文章中也引用了一些其他博主的內容,已標記出處,如果轉載請標明這些博主的出處,我的就無所謂了,謝謝
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容