Zookeeper/Curator客戶端

Curator解決了很多Zookeeper客戶端非常底層的細節開發工作,包括連接重連,反復注冊Watcher和NodeExistsException異常等。此外還有zkClient和Zooleeper自帶的Java API。

添加依賴:
在pom.xml文件中添加如下內容即可。

  <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-recipes</artifactId>
      <version>2.8.0</version>
  </dependency>
  <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-framework</artifactId>
      <version>2.8.0</version>
  </dependency>
  <dependency>
      <groupId>org.apache.curator</groupId>
      <artifactId>curator-client</artifactId>
      <version>2.8.0</version>
  </dependency> 

創建會話:
Curator除了使用一般方法創建會話外,還可以使用fluent風格進行創建。

    import org.apache.curator.RetryPolicy;
    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;

    public class Create_Session_Sample {
        public static void main(String[] args) throws Exception {
            RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
            CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
                    .sessionTimeoutMs(5000).retryPolicy(retryPolicy).namespace("base").build();
            client.start();
            System.out.println("Zookeeper session established. ");        
        }
    }

    運行結果: 
    Zookeeper session1 established. 
    Zookeeper session2 established. 

session會話含有隔離命名空間,即客戶端對Zookeeper上數據節點的任何操作都是相對/base目錄進行的,這有利于實現不同的Zookeeper的業務之間的隔離。當然也可以不設置。

創建節點:
通過使用Fluent風格的接口,開發人員可以進行自由組合來完成各種類型節點的創建。

    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;

    public class Create_Node_Sample {
        public static void main(String[] args) throws Exception {
            String path = "/zk-book/c1";
            CuratorFramework client = CuratorFrameworkFactory.builder()
                    .connectString("127.0.0.1:2181")
                    .sessionTimeoutMs(5000)
                    .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                    .namespace("base")
                    .build();

            client.start();
            
            client.create().creatingParentsIfNeeded()
                           .withMode(CreateMode.EPHEMERAL)
                           .forPath(path, "i am c1".getBytes());

            System.out.println("success create znode: " + path);
        }
    }

    運行結果:
    success create znode: /zk-book/c1

其中,也創建了/base/zk-book/c1的父節點/base/zk-book節點。

刪除節點:

    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.data.Stat;

    public class Del_Data_Sample {
        public static void main(String[] args) throws Exception {
            String path = "/zk-book/c1";
            CuratorFramework client = CuratorFrameworkFactory.builder()
                    .connectString("127.0.0.1:2181")
                    .sessionTimeoutMs(5000)
                    .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                    .namespace("base")
                    .build();

            client.start();

            client.create().creatingParentsIfNeeded()
                           .withMode(CreateMode.EPHEMERAL)
                           .forPath(path, "i am c1".getBytes());

            System.out.println("success create znode: " + path);
            //以上,節點創建完成。

            Stat stat = new Stat();
            System.out.println(new String(client.getData().storingStatIn(stat).forPath(path)));
            client.delete().deletingChildrenIfNeeded().withVersion(stat.getVersion()).forPath(path);
            System.out.println("success delete znode " + path);
        }
    }
    
    運行結果: 
    i am c1
    success delete znode /zk-book/c1

獲取數據:

    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.data.Stat;

    public class Get_Data_Sample {
        public static void main(String[] args) throws Exception {
            String path = "/zk-book";
            CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
                    .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
            client.start();
            client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "i am c1".getBytes());
            Stat stat = new Stat();
            byte b[] = client.getData().storingStatIn(stat).forPath(path);
            System.out.println(new String(b));
        }
    }

    運行結果:
    i am c1

更新數據:

    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.data.Stat;

    public class Set_Data_Sample {
        public static void main(String[] args) throws Exception {
            String path = "/zk-book";
            CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
                    .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
            client.start();
            client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "i am c1".getBytes());            

            stat = client.setData().withVersion(stat.getVersion()).forPath(path);
            System.out.println("Success set node for : " + path + ", new version: "+ stat.getVersion());
        }
    }

    運行結果:  
    Success set node for : /zk-book, new version: 1

異步接口:
如同Zookeeper原生API提供了異步接口,Curator也提供了異步接口。在Zookeeper中,所有的異步通知事件處理都是由EventThread這個線程來處理的,EventThread線程用于串行處理所有的事件通知,其可以保證對事件處理的順序性,但是一旦碰上復雜的處理單元,會消耗過長的處理時間,從而影響其他事件的處理,Curator允許用戶傳入Executor實例,這樣可以將比較復雜的事件處理放到一個專門的線程池中去。

    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.framework.api.BackgroundCallback;
    import org.apache.curator.framework.api.CuratorEvent;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;

    public class Create_Node_Background_Sample {
        static String path = "/zk-book";
        static CuratorFramework client = CuratorFrameworkFactory.builder().connectString("127.0.0.1:2181")
                .sessionTimeoutMs(5000).retryPolicy(new ExponentialBackoffRetry(1000, 3)).build();
        
        static CountDownLatch semaphore = new CountDownLatch(2);
        static ExecutorService tp = Executors.newFixedThreadPool(2);

        public static void main(String[] args) throws Exception {
            client.start();
            System.out.println("Main thread: " + Thread.currentThread().getName());

            client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
                                                     .inBackground(
                                                         new BackgroundCallback(){
                                                             public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
                                                                 System.out.println("event[code: " + event.getResultCode() + ", type: " + event.getType() + "]" + ", Thread of processResult: " + Thread.currentThread().getName());
                                                                 semaphore.countDown();
                                                             }
                                                         }, tp
                                                         )
                                                    .forPath(path, "init".getBytes());

            client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL)
                                                     .inBackground(
                                                         new BackgroundCallback(){
                                                             public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
                                                                 System.out.println("event[code: " + event.getResultCode() + ", type: " + event.getType() + "]" + ", Thread of processResult: " + Thread.currentThread().getName());
                                                                 semaphore.countDown();
                                                             }
                                                         }
                                                         )
                                                     .forPath(path, "init".getBytes());

            semaphore.await();
            tp.shutdown();
        }
    }
    
    運行結果:
    Main thread: main
    event[code: -110, type: CREATE], Thread of processResult: main-EventThread
    event[code: 0, type: CREATE], Thread of processResult: pool-3-thread-1

其中,創建節點的事件由線程池自己處理,而非默認線程處理。

節點監聽:

    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.framework.recipes.cache.NodeCache;
    import org.apache.curator.framework.recipes.cache.NodeCacheListener;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;

    public class NodeCache_Sample {

        public static void main(String[] args) throws Exception {
            String path = "/zk-book/nodecache";
            CuratorFramework client = CuratorFrameworkFactory.builder()
                                                            .connectString("127.0.0.1:2181")
                                                            .sessionTimeoutMs(5000)
                                                            .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                                                            .namespace("base")
                                                            .build();
            client.start();

            //新建節點
            client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path, "i am nodecache".getBytes());
            
            //監聽
            final NodeCache cache = new NodeCache(client, path, false);
            cache.start(true);
            cache.getListenable().addListener(new NodeCacheListener() {
                public void nodeChanged() throws Exception {
                    System.out.println("Node data update, new data: " + new String(cache.getCurrentData().getData()));
                }
            });

            //更新節點
            client.setData().forPath(path, "u".getBytes());
            Thread.sleep(1000);
        }
    }

    運行結果:  
    Node data update, new data: u

當節點數據變更后收到了通知。NodeCache不僅可以監聽數據節點的內容變更,也能監聽指定節點是否存在。

子節點監聽:

    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.framework.recipes.cache.PathChildrenCache;
    import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;
    import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
    import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
    import org.apache.curator.retry.ExponentialBackoffRetry;
    import org.apache.zookeeper.CreateMode;

    public class PathChildrenCache_Sample {

        public static void main(String[] args) throws Exception {
            String path = "/zk-book/nodecache";
            CuratorFramework client = CuratorFrameworkFactory.builder()
                                                            .connectString("127.0.0.1:2181")
                                                            .sessionTimeoutMs(5000)
                                                            .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                                                            .namespace("base")
                                                            .build();
            client.start();

            PathChildrenCache cache = new PathChildrenCache(client, path, true);
            cache.start(StartMode.POST_INITIALIZED_EVENT);
            cache.getListenable().addListener(new PathChildrenCacheListener() {
                public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                    switch (event.getType()) {
                    case CHILD_ADDED:
                        System.out.println("CHILD_ADDED," + event.getData().getPath());
                        break;
                    case CHILD_UPDATED:
                        System.out.println("CHILD_UPDATED," + event.getData().getPath());
                        break;
                    case CHILD_REMOVED:
                        System.out.println("CHILD_REMOVED," + event.getData().getPath());
                        break;
                    default:
                        break;
                    }
                }
            });

            client.create().withMode(CreateMode.PERSISTENT).forPath(path);
            client.create().withMode(CreateMode.PERSISTENT).forPath(path + "/c1");
            client.delete().forPath(path + "/c1");
            Thread.sleep(1000);
        }
    }

    運行結果:
    CHILD_ADDED,/zk-book/c1
    CHILD_REMOVED,/zk-book/c1

監聽節點的子節點,包括新增、數據變化、刪除三類事件。

Master選舉:
借助Zookeeper,開發者可以很方便地實現Master選舉功能,其大體思路如下:選擇一個根節點,如/master_select,多臺機器同時向該節點創建一個子節點/master_select/lock,利用Zookeeper特性,最終只有一臺機器能夠成功創建,成功的那臺機器就是Master。

    import org.apache.curator.framework.CuratorFramework;
    import org.apache.curator.framework.CuratorFrameworkFactory;
    import org.apache.curator.framework.recipes.leader.LeaderSelector;
    import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
    import org.apache.curator.retry.ExponentialBackoffRetry;

    public class Recipes_MasterSelect {

        public static void main(String[] args) throws Exception {
        String path = "/zk-book/nodecache";
            CuratorFramework client = CuratorFrameworkFactory.builder()
                                                            .connectString("127.0.0.1:2181")
                                                            .sessionTimeoutMs(5000)
                                                            .retryPolicy(new ExponentialBackoffRetry(1000, 3))
                                                            .namespace("base")
                                                            .build();
            client.start();

            LeaderSelector selector = new LeaderSelector(client, master_path, new LeaderSelectorListenerAdapter() {
                public void takeLeadership(CuratorFramework client) throws Exception {
                    System.out.println("成為Master角色");
                    Thread.sleep(3000);
                    System.out.println("完成Master操作,釋放Master權利");
                }
            });
            selector.autoRequeue();
            selector.start();
            Thread.sleep(1000);
        }
    }

    運行結果:
    成為Master角色
    完成Master操作,釋放Master權利
    成為Master角色

以上結果會反復循環,并且當一個應用程序完成Master邏輯后,另外一個應用程序的相應方法才會被調用,即當一個應用實例成為Master后,其他應用實例會進入等待,直到當前Master掛了或者推出后才會開始選舉Master。


參考:http://www.cnblogs.com/leesf456/p/6032716.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容