ZooKeeper與Curator註冊和監控

ZooKeeperCurator註冊和監控

僅供參考,不保證正確性

Curator提供了對zookeeper客戶端的封裝,並監控連接狀態和會話session,特別是會話session過期後,curator能夠重新連接zookeeper,並且創建一個新的session

對於zk的使用者來說,session的概念至關重要,如果想了解更多session的說明,請訪問:http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html

 

zk客戶端和zk服務器間主要可能存在下面幾種異常情況:

1.     短暫失去連接:此時客戶端檢測到與服務端的連接已經斷開,但是服務端維護的客戶端session尚未過期,之後客戶端和服務端重新建立了連接;當客戶端重新連接後,由於session沒有過期,zookeeper能夠保證連接恢復後保持正常服務。

2.     失去連接時間很長:此時服務器相對於客戶端的session已經過期了,與先前session相關的watcherephemeral的路徑和數據都會消失;當Curator重新創建了與zk的連接後,會獲取到session expired異常,Curator會銷燬先前的session,並且會創建一個新的session,需要注意的是,與之前session相關的watcherephemeral類型的路徑和數據在新的session中也不會存在,需要開發者在CuratorFramework.getConnectionStateListenable().addListener()中添加狀態監聽事件,對ConnectionState.LOST事件進行監聽,當session過期後,使得之前的session狀態得以恢復。對於ephemeral類型,在客戶端應該保持數據的狀態,以便及時恢復。

3.     客戶端重新啓動:不論先前的zk session是否已經過期,都需要重新創建臨時節點、添加數據和watch事件,先前的session也會在稍後的一段時間內過期。

4.     Zk服務器重新啓動:由於zksession信息存放到了硬盤上,因此重啓後,先前未過期的session仍然存在,在zk服務器啓動後,客戶端與zk服務器創建新的連接,並使用先前的session,與1相同。

5.     需要注意的是,當session過期了,在session過期期間另外的客戶端修改了zk的值,那麼這個修改在客戶端重新連接到zk上時,zk客戶端不會接收到這個修改的watch事件(儘管添加了watch),如果需要嚴格的watch邏輯,就需要在curator的狀態監控中添加邏輯。

 

特別提示:watcher僅僅是一次性的,zookeeper通知了watcher事件後,就會將這個watchersession中刪除,因此,如果想繼續監控,就要添加新的watcher

 

下面提供了對persistentephemeral兩種類型節點的監控方法,其中get方法說明了persistent節點如何監控,而register方法說明了ephemeral類型的節點如何監控。

public class CuratorTest {

    private CuratorFramework zkTools;

    private ConcurrentSkipListSet watchers = newConcurrentSkipListSet();

    private static Charset charset = Charset.forName("utf-8");

   

   

    public CuratorTest() {     

       zkTools = CuratorFrameworkFactory

              .builder()

              .connectString("10.11.21.78:12306")

              .namespace("zk/test")

              .retryPolicy(new RetryNTimes(2000,20000))

              .build();

       zkTools.start();

      

        }  

   

   

    public void addReconnectionWatcher(final String path,final ZookeeperWatcherType watcherType,final CuratorWatcher watcher){

       synchronized (this) {

           if(!watchers.contains(watcher.toString()))//不要添加重複的監聽事件

           {

              watchers.add(watcher.toString());

              System.out.println("add new watcher " + watcher);

              zkTools.getConnectionStateListenable().addListener(newConnectionStateListener() {  

                  @Override

                  public void stateChanged(CuratorFramework client, ConnectionState newState) {

                     System.out.println(newState);

                     if(newState == ConnectionState.LOST){//處理session過期

                         try{

                            if(watcherType == ZookeeperWatcherType.EXITS){

                               zkTools.checkExists().usingWatcher(watcher).forPath(path);

                            }else if(watcherType == ZookeeperWatcherType.GET_CHILDREN){

                               zkTools.getChildren().usingWatcher(watcher).forPath(path);

                            }else if(watcherType == ZookeeperWatcherType.GET_DATA){

                                zkTools.getData().usingWatcher(watcher).forPath(path);

                            }else if(watcherType == ZookeeperWatcherType.CREATE_ON_NO_EXITS){

                                //ephemeral類型的節點session過期了,需要重新創建節點,並且註冊監聽事件,之後監聽事件中,

                                //會處理create事件,將路徑值恢復到先前狀態

                                Stat stat =zkTools.checkExists().usingWatcher(watcher).forPath(path);                             

                                if(stat == null){

                                   System.err.println("to create");

                                   zkTools.create()

                                   .creatingParentsIfNeeded()

                                   .withMode(CreateMode.EPHEMERAL)

                                   .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)

                                   .forPath(path);                                     

                                }

                            }

                         }catch (Exception e) {

                            e.printStackTrace();

                         }

                     }

                  }

              });          

           }

       }

    }

   

 

    public void create() throws Exception{

       zkTools.create()//創建一個路徑

       .creatingParentsIfNeeded()//如果指定的節點的父節點不存在,遞歸創建父節點

       .withMode(CreateMode.PERSISTENT)//存儲類型(臨時的還是持久的)

       .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)//訪問權限

       .forPath("zk/test");//創建的路徑

    }

   

    public void put() throws Exception{

       zkTools.//對路徑節點賦值

       setData().

       forPath("zk/test","hello world".getBytes(Charset.forName("utf-8")));

    }

   

    public void get() throws Exception{

       String path = "zk/test";

       ZKWatch watch = new ZKWatch(path);

       byte[] buffer = zkTools.

                         getData().

                         usingWatcher(watch).forPath(path);

       System.out.println(new String(buffer,charset));

       //添加session過期的監控

       addReconnectionWatcher(path, ZookeeperWatcherType.GET_DATA, watch);

    }  

   

   

    public void register() throws Exception{

      

       String ip = InetAddress.getLocalHost().getHostAddress();

       String registeNode = "zk/register/"+ip;//節點路徑

      

       byte[] data = "disable".getBytes(charset);//節點值

 

       CuratorWatcher watcher = new ZKWatchRegister(registeNode,data);    //創建一個register watcher

      

       Stat stat = zkTools.checkExists().forPath(registeNode);

       if(stat != null){

           zkTools.delete().forPath(registeNode);

       }

       zkTools.create()

       .creatingParentsIfNeeded()          .withMode(CreateMode.EPHEMERAL)

       .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)

       .forPath(registeNode,data);//創建的路徑和值

      

       //添加到session過期監控事件中

       addReconnectionWatcher(registeNode, ZookeeperWatcherType.CREATE_ON_NO_EXITS,watcher);               

       data = zkTools.getData().usingWatcher(watcher).forPath(registeNode);

       System.out.println("get path form zk : "+registeNode+":"+new String(data,charset));

    }

   

    public static void main(String[] args) throws Exception {

       CuratorTest test = new CuratorTest();

       test.get();

       test.register();

       Thread.sleep(10000000000L);

 

    }

   

    public class ZKWatch implements CuratorWatcher{

       private final String path;

      

       public String getPath() {

           return path;

       }

       public ZKWatch(String path) {

           this.path = path;

       }

       @Override

       public void process(WatchedEvent event) throws Exception {

           System.out.println(event.getType());

           if(event.getType() == EventType.NodeDataChanged){

              byte[] data = zkTools.

                     getData().

                     usingWatcher(this).forPath(path);

              System.out.println(path+":"+new String(data,Charset.forName("utf-8")));

           }

       }

      

    }

   

   

    public class ZKWatchRegister implements CuratorWatcher{

       private final String path;

       private byte[] value;

       public String getPath() {

           return path;

       }

       public ZKWatchRegister(String path,byte[] value) {

           this.path = path;

           this.value = value;

       }

       @Override

       public void process(WatchedEvent event) throws Exception {

           System.out.println(event.getType());

           if(event.getType() == EventType.NodeDataChanged){

              //節點數據改變了,需要記錄下來,以便session過期後,能夠恢復到先前的數據狀態

              byte[] data = zkTools.

                     getData().

                     usingWatcher(this).forPath(path);

              value = data;

              System.out.println(path+":"+new String(data,charset));

           }else if(event.getType() == EventType.NodeDeleted){

              //節點被刪除了,需要創建新的節點

              System.out.println(path + ":" + path +" has been deleted.");

              Stat stat = zkTools.checkExists().usingWatcher(this).forPath(path);

              if(stat == null){

                  zkTools.create()

                  .creatingParentsIfNeeded()

                  .withMode(CreateMode.EPHEMERAL)

                  .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)

                  .forPath(path);

              }

           }else if(event.getType() == EventType.NodeCreated){

              //節點被創建時,需要添加監聽事件(創建可能是由於session過期後,curator的狀態監聽部分觸發的)

              System.out.println(path + ":" +" has been created!" + "the current data is " + newString(value));

              zkTools.setData().forPath(path, value);

              zkTools.getData().usingWatcher(this).forPath(path);

           }

       }     

    }

   

    public enum ZookeeperWatcherType{

       GET_DATA,GET_CHILDREN,EXITS,CREATE_ON_NO_EXITS

    }

}


轉載自:http://blog.sina.com.cn/s/blog_616e189f01018axz.html

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