Zookeeper簡介、安裝與使用

這篇文章我試圖簡單易懂的做入門指導,而不是也不想過多描述定義和原理。
Zookeeper Apache 官網
Zookeeper Apache wiki
Zookeeper bird’s eye view
github-Zookeeper

一、是什麼

Apache Zookeeper 原來是Hadoop 的一個子項目,它爲大型分佈式計算提供開源的分佈式配置服務 同步服務 命名註冊 。現在是Apache 的一個獨立頂級項目。它是一個Java程序

Zookeeper的架構通過冗餘服務實現高可用性。因此,如果第一次請求無應答,客戶端就可以詢問另一臺Zookeeper主機。Zookeeper數據結構是一個分層的命名空間,就像一個文件系統那樣。客戶端可以有不同的權限對節點讀寫,從而以這種方式擁有一個共享的配置服務。

引用一下官網的介紹:

Zookeeper 是給分佈式應用使用的一個高性能的協調服務coordination service)。它暴漏公共服務 - 比如命名、配置管理、同步and group services - in a simple interface ,所以你可以用Zookeeper 現成的功能去實現一致性、組管理、集羣領導節點選舉和presence protocols

典型用例

Naming Service(命名服務/目錄服務)

  • 分佈式架構中,有很多應用都對外提供了Service 和一個與之相關的Service路徑 ,且每個應用通常還可能部署了多份在不同的機器上;
    • 也就是說一個Service 有多個提供者;
    • 此時,調用方只要知道Service協議 + IP地址(域名) + 路徑即可成功調用;
  • 這裏要說的是關於IP地址(域名)部分
    顯而易見的是,如果調用方使用硬編碼域名來連接Service 的話,需要將每一個提供者的地址都寫上,然後採用某種策略在調用時選擇其中一個提供者。這會帶來如下問題:
    • 如果服務提供者部署的機器要增加一臺、刪除一臺、或僅修改域名,那麼每一個調用方竟然都不得不去修改其代碼才能實現切換。通常,提供者肯定會變動、調用方也肯定有很多,所以,這是災難……
  • 所以,最好的方式就是有一個協調者/中介者:
    • 協調者:記錄所有提供者的地址、告訴所有調用者應該使用的提供者的地址;
    • 提供者:將自己的地址註冊到協調者上;
    • 調用者:無須再硬編碼提供者地址,而只連接協調者,又協調者告訴它相關提供者的地址;自身只關心Service 路徑就好了;

所以你看,此時Zookeeper 就是命名服務 :它協調了提供者 調用者 提供者 Zookeeper 上註冊其名字 ,而調用者 Zookeeper 獲取提供者 名字 ;—> 協調服務 - Coordination Service

要點

  1. 爲什麼叫Zookeeper?
    Zookeeper wiki:協調分佈式系統這個工作看起來就像動物園做的事,而動物園需要一個管理員。
    • 一類動物就是一類服務器,一類動物一羣就是一類服務器的多節點。
    • 多類動物就是多類服務器,動物園有多類多羣動物,它需要一個管理員。

二、Zookeeper實現算法

僅從上面介紹來說,其實單個Zookeeper 提供的那麼簡單的功能,相信你不會覺得它複雜,甚至大部分人都能輕易寫出一個這樣的程序來。所以:

  • 使用Zookeeper 如果使用單點就沒有意義了,如果這個Zookeeper崩潰了,難道就讓所有提供者和調用者懵逼,從而使整個系統崩潰了嗎?所以一定是多個Zookeeper 實例,也就是分佈式Zookeeper

  • 它的難點在於多個Zookeeper 的通訊,其實現的關鍵部分就在於如何處理分佈式情況下的數據一致性;

Zookeeper 是怎麼做的呢?
Zookeeper 是以Fast Paxos 算法爲基礎的,我們可以瞭解下這個算法。(這裏我先簡單引用下百度百科,這個博文僅僅是要記錄如何快速使用它)

引用百度百科:

Paxos 算法解決的問題是一個分佈式系統如何就某個值(決議)達成一致。一個典型的場景是,在一個分佈式數據庫系統中,如果各節點的初始狀態一致,每個節點執行相同的操作序列,那麼他們最後能得到一個一致的狀態。爲保證每個節點執行相同的命令序列,需要在每一條指令上執行一個“一致性算法”以保證每個節點看到的指令一致。一個通用的一致性算法可以應用在許多場景中,是分佈式計算中的重要問題。因此從20世紀80年代起對於一致性算法的研究就沒有停止過。節點通信存在兩種模型:共享內存(Shared memory)和消息傳遞(Messages passing)。Paxos 算法就是一種基於消息傳遞模型的一致性算法。

三、安裝與配置

完整的安裝嚮導以及參數含義在官網上有,這裏只記錄下我的操作過程。

  1. 下載、解壓
    下載地址:zookeeper-3.4.8.tar.gz

     # 可以直接使用wget下載,也可以在其它地方下載完拷貝過來。這裏解壓到了`/usr/local`下
     wget http://apache.fayea.com/zookeeper/zookeeper-3.4.8/zookeeper-3.4.8.tar.gz
     tar -zxf zookeeper-3.4.8.tar.gz
  2. 修改配置文件(集羣配置)

     cd zookeeper-3.4.8
    
     # 將原有的zoo_sample.cfg拷貝一份,取名叫`zoo.cfg`
     cp conf/zoo_sample.cfg conf/zoo.cfg
    
     # vim修改配置文件
     vim conf/zoo.cfg

    這裏是集羣配置:

    • 修改了dataDir
    • 集羣:增加了最下邊的集羣配置server.N(如果不需要集羣不配置此項,不執行下面的步驟3.
    • 集羣:需要在集羣中每臺機器上都這樣配置。我這裏是兩臺機器192.168.192.128192.168.192.129
    // the directory where the snapshot is stored.
    // do not use /tmp for storage, /tmp here is just example sakes.
    dataDir=/usr/local/zookeeper-3.4.8/data
    
    // There are two port numbers nnnnn.
    // The first followers use to connect to the leader, 
    // and the second is for leader election.
    server.1=192.168.192.128:2555:3555
    server.2=192.168.192.129:2555:3555
  3. 在data目錄下(zoo.cfg中的dataDir目錄)新建一個叫myid的文件,文件內容對應zoo.cfg中的server.xx

    vim myid,操作的是第一臺機器,server.xx是1,所以這裏文件內容是:

    1

    記得將其它機器也配置好相應的這個文件。

  4. 可選:配置JVM參數

    首先,查看<Zookeeper_Home>/bin/zkServer.shstart方法可以看到:定義了變量$JVMFLAGS讓我們設置JVM參數。

    其次,可以在<Zookeeper_Home>/bin/zkEnv.sh中看到:

    if [ -f "$ZOOCFGDIR/java.env" ]
    then
        . "$ZOOCFGDIR/java.env"
    fi

    就是說JVM參數最好配置在<Zookeeper_Home>/conf/java.env中。

    所以,我們改這裏。不過這個文件不存在,先創建一個:vim java.env,代碼如下(這裏我是需要改小一點):

    JVMFLAGS="-Xmx300M -Xms300M"
  5. 可選:配置zookeeper日誌輸出格式、目錄

    首先,在<Zookeeper_Home>/conf/log4j.properties中可以看到:

    log4j.rootLogger=${zookeeper.root.logger}
    
    
    # Add ROLLINGFILE to rootLogger to get log file output
    
    log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender
    log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold}
    log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}
    log4j.appender.ROLLINGFILE.MaxFileSize=${zookeeper.log.maxfilesize}
    log4j.appender.ROLLINGFILE.MaxBackupIndex=${zookeeper.log.maxbackupindex}
    log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n

    還有其它配置沒有全部貼出來,這裏貼出來的是可以滾動存儲的日誌配置,可以看到zookeeper已經給我們準備好了,我們只需要設置相關變量就可以了:

    • ${zookeeper.root.logger}:級別 + 日誌記錄器
    • ${zookeeper.log.dir}/${zookeeper.log.file}:路徑和日誌文件名

    其次,可以在<Zookeeper_Home>/bin/zkServer.sh中看到:

    if [ ! -w "$ZOO_LOG_DIR" ] ; then
    mkdir -p "$ZOO_LOG_DIR"
    fi
    
    ZOO_LOG_FILE=zookeeper-$USER-server-$HOSTNAME.log
    _ZOO_DAEMON_OUT="$ZOO_LOG_DIR/zookeeper-$USER-server-$HOSTNAME.out"
    
    case $1 in
    start)
        echo  -n "Starting zookeeper ... "
        if [ -f "$ZOOPIDFILE" ]; then
          if kill -0 `cat "$ZOOPIDFILE"` > /dev/null 2>&1; then
             echo $command already running as process `cat "$ZOOPIDFILE"`.
             exit 1
          fi
        fi
        nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
        "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
        -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \
        -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
    
       …
    • -Dzookeeper.log.dir=${ZOO_LOG_DIR}
    • -Dzookeeper.log.file=${ZOO_LOG_FILE} 這個上面那個值就行,不改了
    • -Dzookeeper.root.logger=${ZOO_LOG4J_PROP}

    所以,我們需要給上面這2個${***}賦值即可。還是在<Zookeeper_Home>/bin/zkEnv.sh,修改如下:

    if [ "x${ZOO_LOG_DIR}" = "x" ]
    then
        ZOO_LOG_DIR="$ZOOKEEPER_PREFIX/logs"
        // 改爲自己需要的目錄
        ZOO_LOG_DIR="/data/logs/zookeeper"
    fi
    
    if [ "x${ZOO_LOG4J_PROP}" = "x" ]
    then
        ZOO_LOG4J_PROP="INFO,CONSOLE"
        // 改爲自己需要的
        ZOO_LOG4J_PROP="INFO,ROLLINGFILE"
    fi
    
  6. 分別在所有機器上啓動,命令如下

    [root@localhost zookeeper-3.4.8]# bin/zkServer.sh start
    
    ZooKeeper JMX enabled by default
    Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg
    Starting zookeeper ... STARTED  
    
    # 以上,啓動成功!
    
  7. 當然,我們最好還是要驗證一下有沒有真的啓動成功,使用status
    成功狀態如下:

    [root@localhost zookeeper-3.4.8]#  ./bin/zkServer.sh status
    ZooKeeper JMX enabled by default
    Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg
    Mode: leader
    
    [root@localhost zookeeper-3.4.8]# ./bin/zkServer.sh status
    ZooKeeper JMX enabled by default
    Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg
    Mode: follower
    

    如果有問題,會大概顯示如下:

    [root@localhost zookeeper-3.4.8]# ./bin/zkServer.sh status
    ZooKeeper JMX enabled by default
    Using config: /usr/local/zookeeper-3.4.8/bin/../conf/zoo.cfg
    Error contacting service. It is probably not running.

    此時我們可以打開上面自己配置的日誌文件,查看具體信息,並按照信息修改錯誤即可。

四、維護

  1. 手動清理

    Zookeeper文檔中說到:

    These are the snapshot and transactional log files.

    A ZooKeeper server will not remove old snapshots and log files when using the default configuration (see autopurge below), this is the responsibility of the operator.

    就是說在我們配置的dataDir=/usr/local/zookeeper-3.4.8/data目錄下會生成快照文件和事務日誌文件,這些文件需要我們自己負責它的清理工作;

    這裏寫圖片描述

    不過我們當然不是說每次需要手動的去清理這些文件……只要在上面的文檔地址中,將官方提供的腳本代碼按需做成crontab定時任務即可。

  2. zkCli.sh

    啓動腳本./zkCli.sh -server localhost:2181後,可以直接輸入help來查看支持的操作:

    [zk: localhost:2181(CONNECTING) 0] 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

    zkCli.sh其實就是啓動了org.apache.zookeeper.ZooKeeperMain$MyWatcher,可以自己進來看具體的實現:

    static {
        commandMap.put("connect", "host:port");
        commandMap.put("close","");
        commandMap.put("create", "[-s] [-e] path data acl");
        commandMap.put("delete","path [version]");
        commandMap.put("rmr","path");
        commandMap.put("set","path data [version]");
        commandMap.put("get","path [watch]");
        commandMap.put("ls","path [watch]");
        commandMap.put("ls2","path [watch]");
        commandMap.put("getAcl","path");
        commandMap.put("setAcl","path acl");
        commandMap.put("stat","path [watch]");
        commandMap.put("sync","path");
        commandMap.put("setquota","-n|-b val path");
        commandMap.put("listquota","path");
        commandMap.put("delquota","[-n|-b] path");
        commandMap.put("history","");
        commandMap.put("redo","cmdno");
        commandMap.put("printwatches", "on|off");
        commandMap.put("quit","");
        commandMap.put("addauth", "scheme auth");
    }

    官方的Getting Started中有一些具體使用zookeeperStarted


記錄一下介紹的不錯的博客:
ZooKeeper原理及使用 :http://blog.csdn.net/xinguan1267/article/details/38422149
zkCli.sh使用指南:http://blog.csdn.net/ganglia/article/details/11606807?utm_source=tuicool&utm_medium=referral




這個其實是使用Dubbo的前置篇,接下來可以繼續看RPC框架與Dubbo完整使用

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