1. 程式人生 > >zookeeper概念、應用場景、資料組織、叢集搭建、客戶端操作、Java客戶端、curator

zookeeper概念、應用場景、資料組織、叢集搭建、客戶端操作、Java客戶端、curator

  一、zookeeper簡介
  
  1.1 zookeeper簡介
  
  Apache的很多專案以動物來命令,比如Hadoop(大象)、Hive(小蜜蜂)、Pig(豬豬),這些專案都是hadoop生態系統的成員。Hadoop生態系統是為了解決大資料儲存、大資料計算和大資料資料分析的,解決大資料問題的核心思想是分散式,而分散式系統的開發中一個關鍵問題是如何解決資料在不同系統之間的一致性問題。zookeeper顧名思義是動物園管理者,之所以叫動物園管理者是因為zookeeper是用來管理這些分散式系統的。
  
  Hadoop    Hive    Pig
  
  除了管理這些”動物“以外,zookeeper還管理Apche Hbase、Apche Solr等知名專案。zookeeper不是一個完整的產品,它是一個為分散式應用提供一致性服務的軟體,開發者可以使用zookeeper解決分散式系統資料一致性問題,被稱為分散式系統的基石。
  
  1.2 zookeeper架構
  
  zookeeper採用客戶端-伺服器架構,上圖涉及zookeeper中的五個基本概念:
  
  Server
  
  ZooKeeper總體中的一個節點,為客戶端提供所有的服務。
  
  Clinet
  
  客戶端,分散式應用叢集中的一個節點,從伺服器訪問資訊。
  
  Leader
  
  主節點,負責跟蹤從節點狀態和任務的有效性,並分配任務到從節點。
  
  Flower
  
  從節點,對外提供服務。
  
  Ensemble
  
  伺服器組,也就是平常說的叢集,形成ensemble所需的最小節點數為3。
  
  叢集特性:
  
  客戶端可以連線到每個server,每個server的資料完全相同。
  
  每個follower都和leader有連線,接受leader的資料更新操作。
  
  Server記錄事務日誌和快照到持久儲存。
  
  大多數server可用,整體服務就可用(2n+1個服務允許n個失效)。
  
  通俗一點來理解,Zookeeper是由一個leader,多個follower組成的叢集,叢集中儲存一份相同的資料副本,client無論連線到哪個server,資料都是一致的。
  
  1.3 zookeeper資料組織
  
  zookeeper資料特點:一致、有頭、資料樹。
  
  1.4 zookeeper應用場景
  
  叢集管理:利用臨時節點特性,節點關聯的是機器的主機名、IP地址等相關資訊,叢集單點故障也屬於該範疇。
  
  統一命名:利用節點的唯一性和目錄節點樹結構。
  
  配置管理:節點關聯的是配置資訊。
  
  分散式鎖:節點關聯的是要競爭的資源。
  
  二、 Zookeeper基礎
  
  現在玩轉單機版的安裝配置和基礎操作。
  
  2.1 下載
  
  zookeeper下載地址:
  
  https://archive.apache.org/dist/zookeeper/
  
  解壓縮:
  
  tar -zxvf zookeeper-3.4.12.tar.gz
  
  cd zookeeper-3.4.12
  
  cp conf/zoo_sample.cfg conf/zoo.cfg
  
  zoo.cfg為zookeeper的配置檔案,編輯內容如下:
  
  syncLimit=5
  
  dataDir=/usr/local/zookeeper/data
  
  dataLogDir=/usr/local/zookeeper/logs
  
  clientPort=2181
  
  syncLimit: Leader伺服器與follower伺服器之間資訊同步允許的最大時間間隔,如果超過次間隔,預設follower伺服器與leader伺服器之間斷開連結
  
  dataDir: 儲存zookeeper資料的目錄
  
  dataLogDir:儲存zookeeper日誌路徑,當此配置不存在時預設路徑與dataDir一致
  
  clientPort:客戶端訪問zookeeper時經過伺服器端時的埠號
  
  2.2 啟動
  
  執行啟動命令, 看到如下提示說明zookeeper啟動成功:
  
  ➜  ~ sudo zkServer.sh start
  
  Password:
  
  ZooKeeper JMX enabled by default
  
  Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
  
  Starting zookeeper ... STARTED
  
  2.3 客戶端操作
  
  Zookeeper提供了一個客戶端指令碼zkCli.sh(Windows平臺是zkCli.cmd)用於和伺服器互動,進入Zookeeper的bin目錄,執行連線伺服器命令:
  
  ./bin/zkCli.sh
  
  1
  
  看到如下提示說明連線成功:
  
  ......
  
  WATCHER::
  
  WatchedEvent state:SyncConnected type:None path:null
  
  上述命令預設連線本地的Zookeeper,如果需要連線指定的Zookeeper,命令格式如下:
  
  ./bin/zkCli.sh -server ip:port
  
  1
  
  成功連線Zookeeper伺服器以後,就可以執行資料CRUD操作。
  
  輸入help檢視Zookeeper的一些命令的用法提示:
  
  [zk: localhost:2181(CONNECTED) 2] help
  
  ZooKeeper -server host:port cmd args
  
  stat path [watch]
  
  set path data [version]
  
  ls path [watch]
  
  delquota [-n|-b] path
  
  ls2 path [watch]
  
  setAcl path acl
  
  setquota -n|-b val path
  
  history
  
  redo cmdno
  
  printwatches on|off
  
  delete path [version]
  
  sync path
  
  listquota path
  
  rmr path
  
  get path [watch]
  
  create [-s][-e] path data acl
  
  addauth scheme auth
  
  quit
  
  getAcl path
  
  close
  
  connect host:port
  
  2.3.1 建立
  
  建立Zookeeper節點使用create命令,命令格式如下:
  
  create [-s][-e] path data acl
  
  1
  
  -s或-e分別指定節點特性,順序或臨時節點,若不指定,則表示持久節點;acl用來進行許可權控制。
  
  例如,建立名為/zk-node 的節點,值為abc,不加引數預設建立永久節點。
  
  [zk: localhost:2181(CONNECTED) 0] create /zk-node abc
  
  Created /zk-node
  
  建立順序節點:
  
  [zk: localhost:2181(CONNECTED) 4] create -s /zk-order 123
  
  Created /zk-order0000000190
  
  建立臨時節點:
  
  [zk: localhost:2181(CONNECTED) 2] create -e /zk-tmp 123
  
  Created /zk-tmp
  
  臨時節點會在客戶端會話結束後消失,使用quit命令退出後再次連線,/zk-tmp節點會消失。
  
  2.3.2 讀取
  
  ls: 列出路徑下的所有節點
  
  例如,列出根目錄下的所有節點:
  
  ls /
  
  Ls2: 列出子節點的同時列出節點的狀態資訊:
  
  [zk: localhost:2181(CONNECTED) 11] ls2 /
  
  [cluster, zk-node, brokers, zookeeper, zk-order0000000190, admin]
  
  cZxid = 0x0
  
  ctime = Thu Jan 01 08:00:00 CST 1970
  
  mZxid = 0x0
  
  mtime = Thu Jan 01 08:00:00 CST 1970
  
  pZxid = 0x17c5
  
  cversion = 371
  
  dataVersion = 0
  
  aclVersion = 0
  
  ephemeralOwner = 0x0
  
  dataLength = 0
  
  numChildren = 15
  
  get:獲取某一個節點的值:
  
  [zk: localhost:2181(CONNECTED) 5] get /zk-node
  
  123
  
  cZxid = 0x17c0
  
  ctime = Mon Oct 29 20:12:45 CST 2018
  
  mZxid = 0x17c0
  
  mtime = Mon Oct 29 20:12:45 CST 2018
  
  pZxid = 0x17c0
  
  cversion = 0
  
  dataVersion = 0
  
  aclVersion = 0
  
  ephemeralOwner = 0x0
  
  dataLength = 3
  
  numChildren = 0
  
  2.3.3 更新
  
  更新使用set命令,格式如下:
  
  set path data [version]
  
  1
  
  例如:/zk-node的值為123,更新為abc:
  
  set /zk-node abc
  
  1
  
  2.3.4 刪除
  
  刪除Zookeeper上的節點使用delete命令,格式如下:
  
  delete path [version]
  
  1
  
  如果節點下還有子節點,不能直接刪除:
  
  [zk: localhost:2181(CONNECTED) 15] create /a a
  
  Created /a
  
  [zk: localhost:2181(CONNECTED) 16] create /a/b b
  
  Created /a/b
  
  [zk: localhost:2181(CONNECTED) 17] delete /a
  
  Node not empty: /a
  
  [zk: localhost:2181(CONNECTED) 19] delete /a/b
  
  2.4 使用zkui
  
  下載zkui的原始碼:    https://github.com/DeemOpen/zkui
  
  編譯: mvn clean install
  
  拷貝zookeeper配置檔案到target目錄下,和打包出來的jar檔案同級
  
  啟動zkUi : sudo java -jar target/zkui-2.0-SNAPSHOT-jar-with-dependencies.jar
  
  訪問9090埠: http://ip:9090/
  
  使用者名稱和密碼在zkui/config.cfg中
  
  三、Zookeeper叢集
  
  部署Zookeeper時例項的個數為一般為2N+1個,理由是Zookeeper的選舉、增刪改操作都需要半數以上伺服器通過。
  
  準備三臺網路互通的centos伺服器或者虛擬機器,ip分別為:192.168.255.129、192.168.255.132、192.168.255.134。
  
  下載Zookeeper安裝包並解壓到指定目錄,複製一份配置檔案:
  
  cp conf/zoo_sample.cfg conf/zoo.cfg
  
  1
  
  zoo.cfg在三臺伺服器上的配置是一樣的:
  
  tickTime=2000
  
  initLimit=10
  
  syncLimit=5
  
  dataDir=/usr/local/zookeeper/zookeeper-3.4.12/data
  
  dataLogDir=/usr/local/ www.dfgjyl.cn  zookeeper/zookeeper-3.4.12/logs
  
  clientPort=2181
  
  server.1=192.168.255.129:2888:3888
  
  server.2=192.168.255.132:2888:3888
  
  server.3=192.168.255.134:2888:3888
  
  叢集中的每臺ZK server都會有一個用於惟一標識自己的id,myid檔案儲存在dataDir目錄中,指定了當前server的server id。在三臺伺服器上的dataDir目錄下(/usr/local/zookeeper/zookeeper-3.4.12/data)新建myid檔案並寫入一個數字,確保每個節點數字都不一樣:
  
  touch myid
  
  echo 1 > myid
  
  上述配置完成以後啟動Zookeeper:
  
  ./bin/zkServer.sh start
  
  1
  
  如果一切順利,Zookeeper啟動成功,執行jps檢視程序命令,可以看到QuorumPeerMain:
  
  [

[email protected] zookeeper-www.gcyl152.com/ 3.4.12]# jps
  
  11860 Jps
  
  2300 QuorumPeerMain
  
  檢視叢集中各節點的狀態:
  
  [[email protected] zookeeper-3.4.12]# ./bin/zkServer.sh status
  
  ZooKeeper JMX enabled by default
  
  Using config: /usr/local/zookeeper/zookeeper-3.4.12/bin/../conf/zoo.cfg
  
  Mode: leader
  
  三臺機器上Zookeeper的執行狀態以及角色狀態如上圖所示。
  
  四、Java客戶端
  
  4.1 maven座標
  
  <dependency>
  
  <groupId>org.apache.zookeeper</groupId>
  
  <artifactId>zookeeper<www.michenggw.com /artifactId>
  
  <version>3.4.12</version>
  
  <type>pom</type>
  
  </dependency>
  
  4.2 建立會話
  
  AbstractZkClient裡面封裝了連線和關閉Zookeeper的方法.
  
  import org.apache.zookeeper.WatchedEvent;
  
  import org.apache.www.yigouyule2.cn zookeeper.Watcher;
  
  import org.apache.zookeeper.Watcher.Event.EventType;
  
  import org.apache.zookeeper.Watcher.Event.KeeperState;
  
  import org.apache.zookeeper.ZooKeeper;
  
  import java.io.IOException;
  
  import java.util.concurrent.CountDownLatch;
  
  public class AbstractZkClient implements Watcher {
  
  private static final int SESSION_TIME = 5000;
  
  private static CountDownLatch countDownLatch = new CountDownLatch(1);
  
  protected static ZooKeeper www.mcyllpt.com zooKeeper;
  
  public void connect(String hosts) throws IOException, InterruptedException {
  
  zooKeeper = new ZooKeeper(hosts, SESSION_TIME, new ZkClient());
  
  countDownLatch.await(www.mhylpt.com);
  
  }
  
  public void close() throws InterruptedException {
  
  zooKeeper.close();
  
  }
  
  @Override
  
  public void process(WatchedEvent event) {
  
  try {
  
  if (KeeperState.SyncConnected == event.getState()) {
  
  if (Event.EventType.None == event.getType() &&
  
  null == event.getPath()) {
  
  countDownLatch.countDown();
  
  } else if (EventType.NodeCreated == event.getType()) {
  
  System.out.println("("+event.getPath()+")Created");
  
  this.zooKeeper.exists(event.getPath(), true);
  
  } else if (EventType.NodeDeleted==event.getType()) {
  
  System.out.println("Node("+event.getPath()+")Deleted");
  
  this.zooKeeper.exists(event.getPath(), true);
  
  } else if (EventType.NodeDataChanged==event.getType()) {
  
  System.out.println("Node("+event.getPath()
  
  +")DataChanged");
  
  this.zooKeeper.exists(event.getPath(), true);
  
  }
  
  }
  
  } catch (Exception e) {
  
  e.printStackTrace();
  
  4.3 增刪改查
  
  package zookeeper;
  
  import org.apache.log4j.Logger;
  
  import org.apache.zookeeper.CreateMode;
  
  import org.apache.zookeeper.KeeperException;
  
  import org.apache.zookeeper.ZooDefs.Ids;
  
  import java.util.List;
  
  public class ZkClient extends AbstractZkClient {
  
  static String HOSTS = "192.168.255.132:2181,192.168.255.129:2181,192.168.255.134:2181";
  
  public static final Logger logger = Logger.getLogger(ZkClient.class);
  
  /**
  
  * 建立節點
  
  *
  
  * @param path 節點路徑
  
  * @param data 節點value
  
  * @throws KeeperException
  
  * @throws InterruptedException
  
  */
  
  public void create(String path, byte[] data) throws KeeperException, InterruptedException {
  
  this.zooKeeper.create(path, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
  
  }
  
  /**
  
  * 讀取節點資料
  
  *
  
  * @param path 節點路徑
  
  * @throws KeeperException
  
  * @throws InterruptedException
  
  */
  
  public void getChild(String path) throws KeeperException, InterruptedException {
  
  try {
  
  List<String> list = this.zooKeeper.getChildren(path, true);
  
  if (list.isEmpty()) {
  
  logger.info(path + "中沒有節點");
  
  } else {
  
  logger.info(path + "中存在節點");
  
  for (String child : list) {
  
  logger.info("節點為:" + child);
  
  }
  
  }
  
  } catch (KeeperException.NoNodeException e) {
  
  logger.error(e.getStackTrace());
  
  }
  
  }
  
  /**
  
  * 讀取節點資料
  
  *
  
  * @param path 節點路徑
  
  * @return
  
  * @throws KeeperException
  
  * @throws InterruptedException
  
  */
  
  public byte[] getData(String path) throws KeeperException, InterruptedException {
  
  return this.zooKeeper.getData(path, true, null);
  
  }
  
  /**
  
  * 刪除節點
  
  *
  
  * @param path    節點路徑
  
  * @param version 版本號
  
  * @return
  
  */
  
  public void deleteNode(String path, int version) throws KeeperException, InterruptedException {
  
  this.zooKeeper.delete(path, version);
  
  }
  
  /**
  
  * 判斷節點是否存在
  
  *
  
  * @param path
  
  */
  
  public void existNode(String path) throws KeeperException, InterruptedException {
  
  this.zooKeeper.exists(path, true);
  
  }
  
  /**
  
  * 更新節點
  
  *
  
  * @param path
  
  * @param data
  
  * @throws KeeperException
  
  * @throws InterruptedException
  
  */
  
  public void updateNode(String path, byte[] data) throws KeeperException, InterruptedException {
  
  this.zooKeeper.setData(path, data, -1);
  
  }
  
  public static void main(String[] args) throws Exception {
  
  ZkClient zkClient = new ZkClient();
  
  zkClient.connect(HOSTS);
  
  zkClient.getChild("/");
  
  zkClient.existNode("/zk-book");
  
  zkClient.updateNode("/zk-book", "667GG".getBytes());
  
  五、Curator
  
  Curator是Netflix公司開源的一個Zookeeper客戶端,與Zookeeper提供的原生客戶端相比,Curator的抽象層次更高,簡化了Zookeeper客戶端的開發量。Curator官網: http://curator.apache.org/getting-started.html
  
  5.1 版本說明
  
  curator版本    Zookeeper版本
  
  2.x.x    3.4.x、3.5.x
  
  3.x.x    3.5.x
  
  Zookeeper版本為3.4.12,使用2.x.x的curator:
  
  <dependency>
  
  <groupId>org.apache.curator</groupId>
  
  <artifactId>curator-framework</artifactId>
  
  <version>2.12.0<version>
  
  </dependency>
  
  如果curator的版本比Zookeeper版本高,會丟擲如下異常:
  
  Exception in thread "main" org.apache.zookeeper.KeeperException$UnimplementedException: KeeperErrorCode = Unimplemented for ...
  
  1
  
  5.2 建立會話
  
  static String HOSTS = "192.168.255.132:2181,192.168.255.129:2181=";
  
  static CuratorFramework client = CuratorFrameworkFactory.builder()
  
  .connectString(HOSTS)
  
  .sessionTimeoutMs(5000)
  
  .retryPolicy(new ExponentialBackoffRetry(1000,3))
  
  .namespace("pp")
  
  .build();
  
  5.3 基本操作
  
  建立持久節點:
  
  String path = "/a/b";
  
  client.create()
  
  .creatingParentsIfNeeded()
  
  .withMode(CreateMode.PERSISTENT)
  
  .forPath(path,"ab".getBytes());
  
  建立臨時節點:
  
  client.create()
  
  .creatingParentsIfNeeded()
  
  .withMode(CreateMode.EPHEMERAL)
  
  .forPath(path,"temp".getBytes());
  
  獲取節點資料:
  
  client.getData().forPath("/a/b");
  
  1
  
  更新節點資料:
  
  client.setData().forPath("/a/b", "newValue".getBytes());
  
  1
  
  刪除節點:
  
  client.delete().forPath("/a/b");
  
  1
  
  六、總結
  
  上述內容介紹簡介了zookeeper概念、應用場景、資料組織、客戶端操作、Java客戶端以及zookeeper的客戶端框架curator等基礎內容,關於zookeeper學習的進階可以從底層實現原理(paxos演算法、zab協議等理論基礎)和實際應用(實現分散式鎖、Leader選舉等應用場景)兩大方面繼續深入學習。