zookeeper(三)zookeeper的多種客戶端

(一) 使用ZooKeeper原生API

命名空間:

   Chroot特性允許每個客戶端設置一個命名空間,如果一個Zookeeper客戶端設置了Chroot,那麼該客戶端對服務器的任何操作,都將被限定在自己的命名空間下。
   如果我們希望爲應用分配/apps/X下的所有子節點,那麼該應用可以將所有Zookeeper客戶端的Chroot設置爲/apps/X。一旦設置了Chroot後,那麼對於這個客戶端來說,所有的節點路徑都已/apps/X爲跟節點。
客戶端可以在connectString中以添加後綴的方式來設置,如:

192.168.56.101:2181,192.168.56.101:2182,192.168.56.101:2183/apps/X

負載均衡策略:

   StaticHostProvider是ZK默認的一種非常簡單的負載均衡策略,它的表現形式其實類似“Random Robin”策略。StaticHostProvider會從客戶端輸入構成服務器地址,然後通過其next方法從serverAddress中獲取一個服務器地址時,會先將服務器地址打散然後拼裝成一個環形循環列表。
   如原始地址訪問字符爲:“host1,host2,host3,host4,host5”經過打散重新拼裝後會構成環形列表:
在這裏插入圖片描述
初始化的時候currentIndex和lastIndex都是-1。每次嘗試獲取一個服務器地址的時候,都會將currentIndex向前移動1位,如果發現遊標移動超過了整個地址列表的長度,那麼就重置0,回到開始的位置重新開始。對於那些服務器地址列表提供的比較少的場景,StaticHostProvider如果發現當前遊標位置和上次使用過的地址一樣,即當currentIndex和lastIndex相同時,就進行spinDelay毫秒時間的等待。

具體源碼如下:

// 初始化時currentIndex和lastIndex都爲-1
// lastIndex表示當前正在使用的服務器地址位置
private int lastIndex = -1;

// currentIndex表示環形隊列中當前遍歷到的那個元素位置
private int currentIndex = -1;

private void init(Collection<InetSocketAddress> serverAddresses) {
    if (serverAddresses.isEmpty()) {
        throw new IllegalArgumentException(
            "A HostProvider may not be empty!");
    }

    this.serverAddresses.addAll(serverAddresses);
    // 打散集合
    Collections.shuffle(this.serverAddresses);
}

// 獲取服務器地址函數
public InetSocketAddress next(long spinDelay) {
    // currentIndex向前移動一位並與服務器數量做“%”操作
    currentIndex = ++currentIndex % serverAddresses.size();
    // 如果currentIndex和上次訪問的服務器相同,則休眠spinDelay毫秒
    if (currentIndex == lastIndex && spinDelay > 0) {
        try {
            Thread.sleep(spinDelay);
        } catch (InterruptedException e) {
            LOG.warn("Unexpected exception", e);
        }
    } else if (lastIndex == -1) {
        // We don't want to sleep on the first ever connect attempt.
        lastIndex = 0;
    }

    // 根據currentIndex獲取地址
    InetSocketAddress curAddr = serverAddresses.get(currentIndex);
    try {
        // 把地址解析成字符串
        String curHostString = getHostString(curAddr);
        // 根據字符解析所有網絡地址
        List<InetAddress> resolvedAddresses = new ArrayList<InetAddress>(Arrays.asList(this.resolver.getAllByName(curHostString)));
        // 如果List爲空,代表沒有可以解析到的其他主機
        if (resolvedAddresses.isEmpty()) {
            return curAddr;
        }
        // 如果List不爲空,把解析到的集合再次打散
        Collections.shuffle(resolvedAddresses);
        // 返回打散集合的第一條數據
        return new InetSocketAddress(resolvedAddresses.get(0), curAddr.getPort());
    } catch (UnknownHostException e) {
        return curAddr;
    }
}

// 獲取到服務器地址後,連接服務器時調用
public void onConnected(){
    // 讓lastIndex等於currentIndex,相當於lastIndex移動到currentIndex的位置來表示最後一次訪問的服務器
    lastIndex = currentIndex;
}

在使用ZooKeeper的Java客戶端時,經常需要處理幾個問題:

  • Watcher反覆註冊

  • Session超時重連

  • 異常處理

要解決上述的幾個問題,可以自己解決,也可以採用第三方的java客戶端來完成。

(二)使用ZkClient

創建會話:

public ZkClient(final String zkServers, final int sessionTimeout, 
       final int connectionTimeout, final ZkSerializer zkSerializer, 
       final long operationRetryTimeout) 

創建節點:
同步,可以遞歸創建

public String create(String path,Object data,final List<ACL> acl,CreateMode mode)
public void createPersistent(String path,boolean createParents,List<ACL> acl)
public void createPersistent(String path, Object data, List<ACL> acl)
public String createPersistentSequential(String path,Object data,List<ACL> acl)
public void createEphemeral(String path, Object data, List<ACL> acl)
public String createEphemeralSequential(String path,Object data,List<ACL> acl)

刪除節點:
同步,可以提供遞歸刪除

public boolean delete(String path,int version)
public boolean deleteRecursive(String path)

獲取節點:
同步,避免不存在異常

public List<String> getChildren(String path)
public <T> T readData(String path, boolean returnNullIfPathNotExists)
public <T> T readData(String path, Stat stat) 

更新節點:
同步,實現CAS,狀態返回

public void writeData(String path, Object datat, int expectedVersion)
public Stat writeDataReturnStat(String path,Object datat,int expectedVersion)

判斷節點是否存在:

public boolean exists(String path) 

事件監聽:

public List<String> subscribeChildChanges(String path, IZkChildListener listener)

事件通知:

public void handleChildChange(String parentPath, List<String> currentChilds)

(三)使用Curator

   Curator是Netflix公司開源的一個Zookeeper客戶端,與Zookeeper提供的原生客戶端相比,Curator的抽象層次更高,簡化了Zookeeper客戶端的開發量。

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

Curator幾個組成部分

  • Client:是ZooKeeper客戶端的一個替代品,提供了一些底層處理和相關的工具方法。

  • Framework:用來簡化ZooKeeper高級功能的使用,並增加了一些新的功能,比如管理到ZooKeeper集羣的連接,重試處理。

  • Recipes:實現了通用ZooKeeper的recipe,該組件建立在Framework的基礎之上

  • Utilities:各種ZooKeeper的工具類。

  • Errors:異常處理,連接,恢復等。

  • Extensions:recipe擴展

RetryPolicy 連接策略

  • RetryOneTime:只重連一次.
  • RetryNTime:指定重連的次數N.
  • RetryUtilElapsed:指定最大重連超時時間和重連時間間隔,間歇性重連直到超時或者鏈接成功。
  • ExponentialBackoffRetry:基於"backoff(退避)"方式重連,和RetryUtilElapsed的區別是重連的時間間隔是動態的。
  • BoundedExponentialBackoffRetry:同ExponentialBackoffRetry,增加了最大重試次數的控制。

Curator的API

創建會話:

CuratorFrameworkFactory.newClient(String connectString, int sessionTimeoutMs, 
				int connectionTimeoutMs, RetryPolicy retryPolicy)

CuratorFrameworkFactory.builder().connectString("192.168.11.56:2180")  
		    .sessionTimeoutMs(30000).connectionTimeoutMs(30000)  
		    .canBeReadOnly(false)  
		    .retryPolicy(new ExponentialBackoffRetry(1000, Integer.MAX_VALUE))
		    .build();

創建節點:

client.create().creatingParentIfNeeded()
        .withMode(CreateMode.PERSISTENT)
        .withACL(aclList)
        .forPath(path, "hello, zk".getBytes());

刪除節點:

client.delete().guaranteed().deletingChildrenIfNeeded().withVersion(version).forPath(path)

獲取節點:

client.getData().storingStatIn(stat).forPath(path);

client.getChildren().forPath(path);

更新節點:

client.setData().withVersion(version).forPath(path, data)

判斷節點是否存在:

client.checkExists().forPath(path);

設置權限:

Build.authorization(String scheme, byte[] auth)
client.setACL().withVersion(version)
        .withACL(ZooDefs.Ids.CREATOR_ALL_ACL)
        .forPath(path);

監聽器:

  • Cache是curator中對事件監聽的包裝,對事件的監聽可以近似看做是本地緩存視圖和遠程ZK視圖的對比過程
  • NodeCache 節點緩存用於處理節點本身的變化,回調接口NodeCacheListener
  • PathChildrenCache 子節點緩存用於處理節點的子節點變化,回調接口PathChildrenCacheListener
  • TreeCache/NodeCache和PathChildrenCache的結合體,回調接口TreeCacheListener
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章