Zookeeper學習筆記

Zookeeper是一個高性能,分佈式的,開源分佈式應用協調服務。
1、源代碼開發
2、分佈式協調服務
2.1 順序一致性
2.2 原子性
2.3 單一視圖
2.4 可靠性
2.5 實時性
3、高性能
4、簡單的api實現複製的功能


常見應用場景
1、配置中心
2、負載均衡
3、統一命名服務
4、共享鎖


數據模型

集羣角色
1、Leader:接受所有的Follower的提案請求並統一協調發起提案的投票,負責與所有Follower進行內部的數據交換(同步);
2、Follower:直接爲客服端服務並參與提案的投票,同時與Leader進行數據交換(同步);
3、Observer:直接爲客服端服務但不參與提案的投票,同時與Leader進行數據交換(同步);
集羣角色


會話
1、Client初始化連接,狀態轉爲CONNECTING(①)
2、Client與Server成功建立連接,狀態轉爲CONNECTED(②)
3、Client丟失了與Server的連接貨沒有接收到Server的響應,狀態轉爲CONNECTING(③)
4、Client練手另外的Server或連接上了之前的Server,狀態轉爲CONNECTED(②)
5、若會話過期(是Server複製聲明會話過期,狀態轉爲CLOSED(⑤))
6、Cliend也可以主動關閉會話(④),狀態轉爲CLOSED
會話


版本號
cversion
dataversion
aclversion


Watcher


Acl權限控制


Linux下單機Zookeeper搭建
1、下載解壓zookeeper
2、cd conf ; cp zoo_sample.cfg zoo.cfg; vim zoo.cfg
3、修改dataDir=/home/zookeeper/zookeeper1/data
clientPort=5000
4、啓動Zookeeper:
./zkServer.sh start
5、連接Zookeeper,-r只讀
./zkCli.sh [-timeout 0 -r] -server localhost:5000


Linux下Zookeeper集羣搭建
1、創建3臺服務,zookeeper1,zookeeper2,zookeeper3
2、複製修改配置文件zoo.cfg,端口號分別爲5000,5001,5002
#格式server = ip:port(通信端口):port(選舉端口)

所有配置
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/zookeeper/zookeeper1/data
clientPort=5000
tickTime=2000
server.1=10.135.54.100:5100:5101
server.2=10.135.54.100:5102:5103
server.3=10.135.54.100:5104:5105

Zookeeper節點類型
1、持久化節點 create /node_1 data
2、持久化有序節點 create -s /node_1 data
3、臨時節點 create -e /node_1 data session過期,節點就會刪除
4、臨時有序節點 create -s -e /node_1 data


Zookeeper基本操作
1. create [-s] [-e] path data acl 創建節點
2. ls path [watch] 查看節點目錄
3. get path [watch]
“`
cZxid = 0x1000000dd
ctime = Thu Jan 19 10:08:16 CST 2017
mZxid = 0x1000000dd
mtime = Thu Jan 19 10:08:16 CST 2017
pZxid = 0x1000000dd
cversion = 0 子節點版本號
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0 sessionid
dataLength = 5
numChildren = 0

  1. set path data [version] 修改節點數據
  2. delete path [version] 刪除節點
  3. setquota -n|-b val path -n子節點數量,-b數據容量。雖然設置了限制,但是仍然是可以創建成功。同時,會bin/zookeeper.out 輸出警告信息
    7.delquota [-n|-b] path 刪除限制
    1. listquota path 查看限制信息

Zookeeper JavaApi

//連接ZooKeeper 
ZooKeeper zk = new ZooKeeper("139.199.182.26:5001",SESSION_TIMEOUT , new Watcher() {
                public void process(WatchedEvent event) {
                    System.out.println("事件"+event.getType());
                }
            });
/**權限控制
 三種權限範圍:
    OPEN_ACL_UNSAFE : 對所有用戶開放
    READ_ACL_UNSAFE : 只讀
    CREATOR_ALL_ACL: 創建者可以做任何操作
**/
//創建節點
zk.create("/node_1", "hello".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
//更新節點,-1表示所有vesion
zk.setData("/node_1", "hello world".getBytes(), -1);
//刪除節點,-1表示所有vesion
zk.delete("/node_1", -1);
//權限
ArrayList<ACL> list = new ArrayList<ACL>();
ACL acl = new ACL(ZooDefs.Perms.ALL, new Id("digest", DigestAuthenticationProvider.generateDigest("root:123456")));
list.add(acl);
zk.create("/node_1", "hello".getBytes(),list, CreateMode.PERSISTENT);

ZkClient
zkClient主要做了兩件事情。一件是在session loss和session expire時自動創建新的ZooKeeper實例進行重連。另一件是將一次性watcher包裝爲持久watcher。後者的具體做法是簡單的在watcher回調中,重新讀取數據的同時再註冊相同的watcherpublic class ZkClientDemo {

private static String CONNECT_STRING="120.77.22.187:2181,120.77.22.187:2182,120.77.22.187:2183";

private static int SESSION_TIMEOUT=3000;

public static void main(String[] args) {
    ZkClient zkClient=new ZkClient(CONNECT_STRING,SESSION_TIMEOUT,SESSION_TIMEOUT,new MyZkSerializer());
    try {
        zkClient.subscribeChildChanges("/configuration", new IZkChildListener() {
            @Override
            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {
                System.out.println("觸發事件:"+parentPath);
                for(String str:currentChilds){
                    System.out.println(str);
                }
            }
        });
        System.in.read();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        zkClient.close();
    }
}
private static void create(ZkClient zk){
    zk.createPersistent("/node_11/node_11_1/node_11_1_1",true); //遞歸創建節點
}

private static void update(ZkClient zk){
    zk.writeData("/node_11","zyz");
}

private static void delete(ZkClient zk){
    boolean bool=zk.deleteRecursive("/node_11");
    System.out.println(bool);
}

private static void subWatch(ZkClient zk){
    if(!zk.exists("/node_11")) {
        zk.createPersistent("/node_11");
    }
    //數據訂閱事件
    zk.subscribeDataChanges("/node_11", new IZkDataListener() {
        @Override
        public void handleDataChange(String dataPath, Object data) throws Exception {
            System.out.println("觸發事件:"+dataPath+"->"+data);
        }

        @Override
        public void handleDataDeleted(String dataPath) throws Exception {
            System.out.println("觸發刪除事件:"+dataPath);
        }
    });
}

}
public class MyZkSerializer implements ZkSerializer{
@Override
public byte[] serialize(Object data) throws ZkMarshallingError {
try {
return String.valueOf(data).getBytes(“UTF-8”);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}

@Override
public Object deserialize(byte[] bytes) throws ZkMarshallingError {
    try {
        return new String(bytes, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return null;
}

}


Curator
Curator是Netflix開源的一套ZooKeeper客戶端框架
封裝ZooKeeper client與ZooKeeper server之間的連接處理;
提供了一套Fluent風格的操作API;
提供ZooKeeper各種應用場景(recipe, 比如共享鎖服務, 集羣領導選舉機制)的抽象封裝.

Curator幾個組成部分
Client: 是ZooKeeper客戶端的一個替代品, 提供了一些底層處理和相關的工具方法.
Framework: 用來簡化ZooKeeper高級功能的使用, 並增加了一些新的功能, 比如管理到ZooKeeper集羣的連接, 重試處理
Recipes: 實現了通用ZooKeeper的recipe, 該組件建立在Framework的基礎之上
Utilities:各種ZooKeeper的工具類 Errors: 異常處理, 連接, 恢復等. Extensions: recipe擴展

Curator 重試策略
1. ExponentialBackoffRetry 衰減重試
2. RetryNTimes 指定最大重試次數
3. RetryOneTime 只重試一次
4. RetryUntilElased 一直重試直到規定時間

監聽器
New watcher
CuratorListener
pathChildCacheListenner
NodeCacheListenner
TreeCacheListenner

public class CuratorDemo {

    private static String CONNECT_STRING="120.77.22.187:2181,120.77.22.187:2182,120.77.22.187:2183";

    private static int SESSION_TIMEOUT=3000;

    public static void main(String[] args) throws Exception {
        //TODO 連接zookeeper
        CuratorFramework framework=CuratorFrameworkFactory.
                newClient(CONNECT_STRING,SESSION_TIMEOUT,SESSION_TIMEOUT,new ExponentialBackoffRetry(1000,10));

        framework.start();
//        create(framework);
//        update(framework);
//        delete(framework);
//        transaction(framework);
        listener2(framework);
        System.in.read();
        System.out.println(framework.getState()); //獲取連接狀態
    }

    private static void create(CuratorFramework cf){
        try {
            String rs=cf.create().withMode(CreateMode.EPHEMERAL).inBackground().forPath("/node_14/node_14_1","zzy".getBytes());
            System.out.println(rs);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            cf.close();
        }
    }

    private static void update(CuratorFramework cf) throws Exception {
        Stat stat=cf.setData().forPath("/node_13/node_13_1","xyz".getBytes());
        System.out.println(stat);
        cf.close();
    }

    private static void delete(CuratorFramework cf){
        try {
            cf.delete().deletingChildrenIfNeeded().forPath("/node_2"); //遞歸刪除的話,則輸入父節點
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            cf.close();
        }
    }

    private static void transaction(CuratorFramework cf){
        try {
            //事務處理, 事務會自動回滾
            Collection<CuratorTransactionResult> results=cf.inTransaction().create().
                    forPath("/node_2").and().create().forPath("/node_3").and().commit();
            for(CuratorTransactionResult result:results){
                System.out.println(result.getResultStat()+"->"+result.getForPath()+"->"+result.getType());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void listener(CuratorFramework cf){
        try {
            cf.getData().usingWatcher(new CuratorWatcher() {
                @Override
                public void process(WatchedEvent event) throws Exception {
                    System.out.println("觸發事件"+event.getType());
                }
            }).forPath("/node_3"); //通過CuratorWatcher 去監聽指定節點的事件, 只監聽一次

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
           // cf.close();
        }
    }

    private static void listener2(CuratorFramework cf) throws Exception {
        //子節點監聽
        PathChildrenCache childrenCache=new PathChildrenCache(cf,"/node_3",true);
        //NodeCache nodeCache;
        childrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);
        childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                System.out.println(event.getType()+"事件監聽2");
            }
        });
       // cf.getCuratorListenable().addListener();


    }
}

ZAB協議
ZAB協議是爲分佈式協調服務zookeeper專門設計的一種支持崩潰恢復的原子廣播協議
在 ZooKeeper 中,主要依賴 ZAB 協議來實現分佈式數據一致性,基於該協議,ZooKeeper 實現了一種主備模式的系統架構來保持集羣中各個副本之間的數據一致性。

ZAB協議包括兩種基本模式
1. 崩潰恢復
leader出現網絡中端、或者服務不可用。Zab協議就會進入恢復模式
投票數量過半
當leader選舉出來以後,zab協議就會進入消息廣播狀態
2. 消息廣播
消息同步
查看事務日誌的命令

java -cp ../../zookeeper-3.4.9/zookeeper-3.4.9.jar:../../zookeeper-3.4.9/lib/slf4j-api-1.6.1.jar org.apache.zookeeper.server.LogFormatter log.200000f73

Zookeeper注意事項
集羣裏分三種角色: Leader, Follower和Observer。Leader和Follower參與投票,Observer只會『聽』投票的結果,不參與投票
一個集羣容忍掛掉的節點數的等式爲 N = 2F + 1,N爲投票集羣節點數,F爲能同時容忍失敗節點數
一個寫操作需要半數以上的節點ack,所以集羣節點數越多,整個集羣可以抗掛點的節點數越多(越可靠),但是吞吐量越差

集羣服務器數
網絡
內存
事務日誌清理
日誌,jvm配置
日誌位置
地址

不要強依賴Zookeeper

不要使用Zookeeper做細粒度鎖

不要將很多東西塞到Zookeeper裏

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