Zookeeper客戶端Curator使用詳解

[TOC]

Zookeeper客戶端Curator使用詳解

前提

維護(hù)多個博客比較麻煩憔涉,簡書和博客園放棄維護(hù)订框,后續(xù)在個人博客持續(xù)更新:

簡介

Curator是Netflix公司開源的一套zookeeper客戶端框架,解決了很多Zookeeper客戶端非常底層的細(xì)節(jié)開發(fā)工作兜叨,包括連接重連穿扳、反復(fù)注冊Watcher和NodeExistsException異常等等。Patrixck Hunt(Zookeeper)以一句“Guava is to Java that Curator to Zookeeper”給Curator予高度評價国旷。
引子和趣聞:
Zookeeper名字的由來是比較有趣的矛物,下面的片段摘抄自《從PAXOS到ZOOKEEPER分布式一致性原理與實踐》一書:
Zookeeper最早起源于雅虎的研究院的一個研究小組。在當(dāng)時议街,研究人員發(fā)現(xiàn)泽谨,在雅虎內(nèi)部很多大型的系統(tǒng)需要依賴一個類似的系統(tǒng)進(jìn)行分布式協(xié)調(diào)璧榄,但是這些系統(tǒng)往往存在分布式單點問題特漩。所以雅虎的開發(fā)人員就試圖開發(fā)一個通用的無單點問題的分布式協(xié)調(diào)框架。在立項初期骨杂,考慮到很多項目都是用動物的名字來命名的(例如著名的Pig項目)涂身,雅虎的工程師希望給這個項目也取一個動物的名字。時任研究院的首席科學(xué)家Raghu Ramakrishnan開玩笑說:再這樣下去搓蚪,我們這兒就變成動物園了蛤售。此話一出,大家紛紛表示就叫動物園管理員吧——因為各個以動物命名的分布式組件放在一起妒潭,雅虎的整個分布式系統(tǒng)看上去就像一個大型的動物園了悴能,而Zookeeper正好用來進(jìn)行分布式環(huán)境的協(xié)調(diào)——于是,Zookeeper的名字由此誕生了雳灾。

Curator無疑是Zookeeper客戶端中的瑞士軍刀漠酿,它譯作"館長"或者''管理者'',不知道是不是開發(fā)小組有意而為之谎亩,筆者猜測有可能這樣命名的原因是說明Curator就是Zookeeper的館長(腦洞有點大:Curator就是動物園的園長)炒嘲。
Curator包含了幾個包:
curator-framework:對zookeeper的底層api的一些封裝
curator-client:提供一些客戶端的操作,例如重試策略等
curator-recipes:封裝了一些高級特性匈庭,如:Cache事件監(jiān)聽夫凸、選舉、分布式鎖阱持、分布式計數(shù)器夭拌、分布式Barrier等
Maven依賴(使用curator的版本:2.12.0,對應(yīng)Zookeeper的版本為:3.4.x,如果跨版本會有兼容性問題鸽扁,很有可能導(dǎo)致節(jié)點操作失敗):

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

Curator的基本Api

創(chuàng)建會話

1.使用靜態(tài)工程方法創(chuàng)建客戶端

一個例子如下:

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework client =
CuratorFrameworkFactory.newClient(
                        connectionInfo,
                        5000,
                        3000,
                        retryPolicy);

newClient靜態(tài)工廠方法包含四個主要參數(shù):

參數(shù)名 說明
connectionString 服務(wù)器列表道逗,格式host1:port1,host2:port2,...
retryPolicy 重試策略,內(nèi)建有四種重試策略,也可以自行實現(xiàn)RetryPolicy接口
sessionTimeoutMs 會話超時時間,單位毫秒献烦,默認(rèn)60000ms
connectionTimeoutMs 連接創(chuàng)建超時時間,單位毫秒吏夯,默認(rèn)60000ms

2.使用Fluent風(fēng)格的Api創(chuàng)建會話

核心參數(shù)變?yōu)榱魇皆O(shè)置即横,一個列子如下:

        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client =
        CuratorFrameworkFactory.builder()
                .connectString(connectionInfo)
                .sessionTimeoutMs(5000)
                .connectionTimeoutMs(5000)
                .retryPolicy(retryPolicy)
                .build();

3.創(chuàng)建包含隔離命名空間的會話

為了實現(xiàn)不同的Zookeeper業(yè)務(wù)之間的隔離噪生,需要為每個業(yè)務(wù)分配一個獨立的命名空間(NameSpace),即指定一個Zookeeper的根路徑(官方術(shù)語:為Zookeeper添加“Chroot”特性)东囚。例如(下面的例子)當(dāng)客戶端指定了獨立命名空間為“/base”页藻,那么該客戶端對Zookeeper上的數(shù)據(jù)節(jié)點的操作都是基于該目錄進(jìn)行的桨嫁。通過設(shè)置Chroot可以將客戶端應(yīng)用與Zookeeper服務(wù)端的一課子樹相對應(yīng),在多個應(yīng)用共用一個Zookeeper集群的場景下份帐,這對于實現(xiàn)不同應(yīng)用之間的相互隔離十分有意義璃吧。

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework client =
        CuratorFrameworkFactory.builder()
                .connectString(connectionInfo)
                .sessionTimeoutMs(5000)
                .connectionTimeoutMs(5000)
                .retryPolicy(retryPolicy)
                .namespace("base")
                .build();

啟動客戶端

當(dāng)創(chuàng)建會話成功畜挨,得到client的實例然后可以直接調(diào)用其start( )方法:

client.start();

數(shù)據(jù)節(jié)點操作

創(chuàng)建數(shù)據(jù)節(jié)點

Zookeeper的節(jié)點創(chuàng)建模式:

  • PERSISTENT:持久化
  • PERSISTENT_SEQUENTIAL:持久化并且?guī)蛄刑?/li>
  • EPHEMERAL:臨時
  • EPHEMERAL_SEQUENTIAL:臨時并且?guī)蛄刑?/li>

**創(chuàng)建一個節(jié)點噩凹,初始內(nèi)容為空 **

client.create().forPath("path");

注意:如果沒有設(shè)置節(jié)點屬性,節(jié)點創(chuàng)建模式默認(rèn)為持久化節(jié)點逮刨,內(nèi)容默認(rèn)為空

創(chuàng)建一個節(jié)點幻赚,附帶初始化內(nèi)容

client.create().forPath("path","init".getBytes());

創(chuàng)建一個節(jié)點,指定創(chuàng)建模式(臨時節(jié)點)箩退,內(nèi)容為空

client.create().withMode(CreateMode.EPHEMERAL).forPath("path");

創(chuàng)建一個節(jié)點佳谦,指定創(chuàng)建模式(臨時節(jié)點),附帶初始化內(nèi)容

client.create().withMode(CreateMode.EPHEMERAL).forPath("path","init".getBytes());

創(chuàng)建一個節(jié)點啥刻,指定創(chuàng)建模式(臨時節(jié)點),附帶初始化內(nèi)容娄涩,并且自動遞歸創(chuàng)建父節(jié)點

client.create()
      .creatingParentContainersIfNeeded()
      .withMode(CreateMode.EPHEMERAL)
      .forPath("path","init".getBytes());

這個creatingParentContainersIfNeeded()接口非常有用映跟,因為一般情況開發(fā)人員在創(chuàng)建一個子節(jié)點必須判斷它的父節(jié)點是否存在,如果不存在直接創(chuàng)建會拋出NoNodeException球恤,使用creatingParentContainersIfNeeded()之后Curator能夠自動遞歸創(chuàng)建所有所需的父節(jié)點荸镊。

刪除數(shù)據(jù)節(jié)點

刪除一個節(jié)點

client.delete().forPath("path");

注意躬存,此方法只能刪除葉子節(jié)點,否則會拋出異常诵叁。

刪除一個節(jié)點钦椭,并且遞歸刪除其所有的子節(jié)點

client.delete().deletingChildrenIfNeeded().forPath("path");

刪除一個節(jié)點碑诉,強(qiáng)制指定版本進(jìn)行刪除

client.delete().withVersion(10086).forPath("path");

刪除一個節(jié)點,強(qiáng)制保證刪除

client.delete().guaranteed().forPath("path");

guaranteed()接口是一個保障措施德挣,只要客戶端會話有效快毛,那么Curator會在后臺持續(xù)進(jìn)行刪除操作唠帝,直到刪除節(jié)點成功。

注意:上面的多個流式接口是可以自由組合的贴铜,例如:

client.delete().guaranteed().deletingChildrenIfNeeded().withVersion(10086).forPath("path");

讀取數(shù)據(jù)節(jié)點數(shù)據(jù)

讀取一個節(jié)點的數(shù)據(jù)內(nèi)容

client.getData().forPath("path");

注意,此方法返的返回值是byte[ ];

讀取一個節(jié)點的數(shù)據(jù)內(nèi)容徘意,同時獲取到該節(jié)點的stat

Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("path");

更新數(shù)據(jù)節(jié)點數(shù)據(jù)

更新一個節(jié)點的數(shù)據(jù)內(nèi)容

client.setData().forPath("path","data".getBytes());

注意:該接口會返回一個Stat實例

更新一個節(jié)點的數(shù)據(jù)內(nèi)容轩褐,強(qiáng)制指定版本進(jìn)行更新

client.setData().withVersion(10086).forPath("path","data".getBytes());

檢查節(jié)點是否存在

client.checkExists().forPath("path");

注意:該方法返回一個Stat實例把介,用于檢查ZNode是否存在的操作. 可以調(diào)用額外的方法(監(jiān)控或者后臺處理)并在最后調(diào)用forPath( )指定要操作的ZNode

獲取某個節(jié)點的所有子節(jié)點路徑

client.getChildren().forPath("path");

注意:該方法的返回值為List<String>,獲得ZNode的子節(jié)點Path列表。 可以調(diào)用額外的方法(監(jiān)控地技、后臺處理或者獲取狀態(tài)watch, background or get stat) 并在最后調(diào)用forPath()指定要操作的父ZNode

事務(wù)

CuratorFramework的實例包含inTransaction( )接口方法秒拔,調(diào)用此方法開啟一個ZooKeeper事務(wù). 可以復(fù)合create, setData, check, and/or delete 等操作然后調(diào)用commit()作為一個原子操作提交砂缩。一個例子如下:

client.inTransaction().check().forPath("path")
      .and()
      .create().withMode(CreateMode.EPHEMERAL).forPath("path","data".getBytes())
      .and()
      .setData().withVersion(10086).forPath("path","data2".getBytes())
      .and()
      .commit();

異步接口

上面提到的創(chuàng)建、刪除妹懒、更新双吆、讀取等方法都是同步的,Curator提供異步接口匾竿,引入了BackgroundCallback接口用于處理異步接口調(diào)用之后服務(wù)端返回的結(jié)果信息蔚万。BackgroundCallback接口中一個重要的回調(diào)值為CuratorEvent,里面包含事件類型昵慌、響應(yīng)嗎和節(jié)點的詳細(xì)信息淮蜈。

CuratorEventType

事件類型 對應(yīng)CuratorFramework實例的方法
CREATE #create()
DELETE #delete()
EXISTS #checkExists()
GET_DATA #getData()
SET_DATA #setData()
CHILDREN #getChildren()
SYNC #sync(String,Object)
GET_ACL #getACL()
SET_ACL #setACL()
WATCHED #Watcher(Watcher)
CLOSING #close()

響應(yīng)碼(#getResultCode())

響應(yīng)碼 意義
0 OK礁芦,即調(diào)用成功
-4 ConnectionLoss,即客戶端與服務(wù)端斷開連接
-110 NodeExists闺魏,即節(jié)點已經(jīng)存在
-112 SessionExpired俯画,即會話過期

一個異步創(chuàng)建節(jié)點的例子如下:

Executor executor = Executors.newFixedThreadPool(2);
client.create()
      .creatingParentsIfNeeded()
      .withMode(CreateMode.EPHEMERAL)
      .inBackground((curatorFramework, curatorEvent) -> {      System.out.println(String.format("eventType:%s,resultCode:%s",curatorEvent.getType(),curatorEvent.getResultCode()));
      },executor)
      .forPath("path");

注意:如果#inBackground()方法不指定executor,那么會默認(rèn)使用Curator的EventThread去進(jìn)行異步處理泡仗。

Curator食譜(高級特性)

提醒:首先你必須添加curator-recipes依賴猜憎,下文僅僅對recipes一些特性的使用進(jìn)行解釋和舉例胰柑,不打算進(jìn)行源碼級別的探討

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>2.12.0</version>
        </dependency>

重要提醒:強(qiáng)烈推薦使用ConnectionStateListener監(jiān)控連接的狀態(tài),當(dāng)連接狀態(tài)為LOST崩瓤,curator-recipes下的所有Api將會失效或者過期踩官,盡管后面所有的例子都沒有使用到ConnectionStateListener。

緩存

Zookeeper原生支持通過注冊Watcher來進(jìn)行事件監(jiān)聽颖系,但是開發(fā)者需要反復(fù)注冊(Watcher只能單次注冊單次使用)蛋逾。Cache是Curator中對事件監(jiān)聽的包裝,可以看作是對事件監(jiān)聽的本地緩存視圖,能夠自動為開發(fā)者處理反復(fù)注冊監(jiān)聽亏钩。Curator提供了三種Watcher(Cache)來監(jiān)聽結(jié)點的變化欺旧。

Path Cache

Path Cache用來監(jiān)控一個ZNode的子節(jié)點. 當(dāng)一個子節(jié)點增加, 更新栅哀,刪除時, Path Cache會改變它的狀態(tài)戳晌, 會包含最新的子節(jié)點痴柔, 子節(jié)點的數(shù)據(jù)和狀態(tài),而狀態(tài)的更變將通過PathChildrenCacheListener通知豪嚎。

實際使用時會涉及到四個類:

  • PathChildrenCache
  • PathChildrenCacheEvent
  • PathChildrenCacheListener
  • ChildData

通過下面的構(gòu)造函數(shù)創(chuàng)建Path Cache:

public PathChildrenCache(CuratorFramework client, String path, boolean cacheData)

想使用cache侈询,必須調(diào)用它的start方法糯耍,使用完后調(diào)用close方法。 可以設(shè)置StartMode來實現(xiàn)啟動的模式

......to be continue

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啦租,一起剝皮案震驚了整個濱河市篷角,隨后出現(xiàn)的幾起案子系任,更是在濱河造成了極大的恐慌,老刑警劉巖嘉蕾,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霜旧,死亡現(xiàn)場離奇詭異挂据,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掷倔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門勒葱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凛虽,你說我怎么就攤上這事涩维。” “怎么了瓦阐?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵睡蟋,是天一觀的道長戳杀。 經(jīng)常有香客問我,道長信卡,這世上最難降的妖魔是什么傍菇? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮牵触,結(jié)果婚禮上咐低,老公的妹妹穿的比我還像新娘见擦。我一直安慰自己,他們只是感情好鲤屡,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布执俩。 她就那樣靜靜地躺著癌刽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪衡奥。 梳的紋絲不亂的頭發(fā)上矮固,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音盹兢,去河邊找鬼守伸。 笑死,一個胖子當(dāng)著我的面吹牛见芹,可吹牛的內(nèi)容都是我干的蠢涝。 我是一名探鬼主播和二,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼儿咱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起怠缸,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤揭北,失蹤者是張志新(化名)和其女友劉穎吏颖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疚俱,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡呆奕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了绳泉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姆泻。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡拇勃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛔琅,到底是詐尸還是另有隱情峻呛,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布寨躁,位于F島的核電站职恳,受9級特大地震影響方面,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜操禀,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一横腿、第九天 我趴在偏房一處隱蔽的房頂上張望耿焊。 院中可真熱鬧,春花似錦罗侯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垂睬。三九已至,卻和暖如春钳枕,著一層夾襖步出監(jiān)牢的瞬間赏壹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留菩佑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像瞧哟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子咧党,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容