Zookeeper_原生API操作(二)
生活随笔
收集整理的這篇文章主要介紹了
Zookeeper_原生API操作(二)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在有curator這個框架之前的時候,然后zookeeper怎么去實現分布式鎖,就是利用剛才的那種方案,就是可以去實現,你會有各種各樣的疑問,你剛才講的設計是得先get一下,你不如直接create,因為創建性能還不是那么高的,其實你有各種各樣的疑問你得去了解他,你要知道怎么做才能快,你要為性能考慮才能那樣去做,之前這塊也說了,我們往上走一走吧,很多介紹的東西,你當時有一個大體的概念,但是沒有一個很詳細的概念,剛才在這里為什么那么去說,就是他的數據基本上是存在內存中的,就是他的一些視圖模型,單一視圖,都是在內存中的,所以說,在JMater百分百讀的場景下,并發可以達到這個,12到13萬,你想想吧,所以get的性能是很高的,我一個client端,只要網絡沒有太大的問題,沒有延遲的情況下,get一下馬上就能得到反饋,這個效率是非常高的,所以之前我先get一下有沒有,你不能get都不get就直接create,等著那塊就拋異常了,等著報異常就不好,一般我們不那么去做,就是讓性能能更好,都是先去get一下,先去內存里去取,看有沒有這條數據,然后再決定到底要不要create,極少量的情況下會發生兩個同時get,這個時候創建點肯定有一個先后順序的,肯定是一個成功,一個失敗的,這就是zookeeper怎么去實現分布式這塊
然后再往下看,還是要看這個API,其實倒不如直接講Curator框架,如果不講這些東西,你直接拿去用的話,面試的時候是很吃虧的,zookeeper怎么去實現分布式鎖的,你得講明白是用臨時節點,為什么啊,因為臨時節點效率也高,第二點我什么要用get啊,因為zookeeper是有這么一個特性,數據是存在內存中的,get的性能是非常高的,所以說我這么去做,是有理由有依據的,然后咱們再去看一下API,這塊要說的一點,咱們是不允許遞歸創建節點的,你父節點沒有創建的情況下是不允許創建子節點的,咱們可以試一試
我們先ls /一下
我把testRoot刪掉, rmr /testRoot
然后ls /,當前只有一個zookeeper節點,不允許遞歸創建節點,父節點不存在的情況下是不允許創建節點的,它是不允許支持序列化框架的,比如我們能不能做這么一件事,user的key就換一個主鍵ID,value就放一個user對象,或者就一個JAVA對象,這個zookeeper是不支持的,以后它是不支持序列化的,你可以去使用一些第三方的序列化框架,比如Hessian,Kryo序列號框架,就是這些序列號框架,Kryo框架的性能也是非常好的,包括dubbo中也是采用了一種方式,第三個參數是一個權限節點,模模糊糊不知道是什么概念,Ids它是一個API,它是一個靜態的,然后他有一個OPEN_ACL_UNSAFE,開放權限即可,這個表示開放權限,后期有一些參數,基本上我從來沒有關注過,包括很多API封裝的時候,第三方框架就寫死了,一般在權限沒有太高要求的情況下是沒有必要關注的,你就不用費這個事去關注了,相關的問題我們通過網絡,不會考慮太詳細,只不過是他提供的功能是這樣,這個不用關注,然后最后一個是創建節點類型,有4種類型,有持久化的,有持久化順序的,有個先后順序,當然會有個什么問題,你可能調一個方法,一個client端C1,現在10個節點,那你這10個節點要有順序,可能網絡走到這個時候,第一個創建可能繞了一圈,第二個可能是沒有先后順序的,如果你有順序的話你就可以去看看,你這個zookeeper要做一個隊列,你可以create一個sequential,就是你要那他做一個隊列的一個事,其實很多技術可以提供一個解決方案,就是Redis有一個list類型,它是有一些附加的功能,只不過這種實現起來會比較難,你要抓住最核心的作用是干什么的,然后你輔助的功能,很簡單的情況下你可以去用,我們不用消息隊列,你想實現基于內存的,很多問題你自己可以去解決,兩種解決你可以自己去做,包括這個zookeeper也一樣,他想實現一個隊列,他可以去提供這個接口,一般有沒有人這么去做呢,這是我強調的一個問題,他不僅僅能實現一點,我只是大概提一下,然后往下走,還有一種是異步的方式,節點也分同步和異步,同步的方式就是我創建完了之后,我發一個請求到zookeeper上,我這邊得到一個返回值,這是同步的方式,我的代碼肯定是在這一點延遲,創建調API,同步方式,異步呢,代碼是直接往下走,我又起了一個額外的線程,去監聽zookeeper的返回,這是可以去實現的,這里有一個異步方式
package com.learn.zookeeper.base;import java.util.concurrent.CountDownLatch;import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;/*** 這就是關于zookeeper增刪改查操作* 簡單的講一下* 里面有一些特殊的方法* 每一套API* 無論是exist,delete,還是get* 都有兩套* 一種是同步的方式* 一種是回調的方式* 然后基本上去操作API呢* 會有一些限制* 比如說不能遞歸的去創建啊* 不能遞歸的去刪除* 去掉getChildren方法的時候* 十六進制JAVA怎么表示來著* 有點不記得了* 用byte[]字節數組* get只能取得一層* 你不能把下面的節點也取得到* 有很多限制* 這是關于咱們zookeeper的API* * @author Leon.Sun**/
public class ZookeeperBase {static final String CONNECT_ADDR = "59.110.138.145:2181";
// static final String CONNECT_ADDR = "192.168.80.88:2181,192.168.80.87:2181,192.168.80.86:2181";static final int SESSION_OUTTIME = 2000;//ms /** 信號量,阻塞程序執行,用于等待zookeeper連接成功,發送成功信號 */static final CountDownLatch connectedSemaphore = new CountDownLatch(1);public static void main(String[] args) throws Exception{ZooKeeper zk = new ZooKeeper(CONNECT_ADDR, SESSION_OUTTIME, new Watcher(){@Overridepublic void process(WatchedEvent event) {KeeperState keeperState = event.getState();EventType eventType = event.getType();//如果是建立連接if(KeeperState.SyncConnected == keeperState){if(EventType.None == eventType){try {System.out.println("開始連接.....");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("zk 建立連接");//如果建立連接成功,則發送信號量,讓后續阻塞程序向下執行connectedSemaphore.countDown();}}}});//進行阻塞connectedSemaphore.await();System.out.println("執行啦..");Thread.sleep(5000);//創建父節點
// String ret = zk.create("/testRoot", "testRoot11".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// System.out.println(ret);//創建子節點/*** 現在我想又一次創建* 肯定是沒有/testRoot這個節點的* 就像創建/children* 行嗎* 肯定是不行的* 咱們運行一下吧* KeeperException$NoNodeException: KeeperErrorCode = NoNode for /testRoot/children* 就是你沒有父節點你就想創建子節點* zookeeper原生的API是不支持的* 怎么去實現的* 你得先判斷一下* 先來個if再來個else* 其實Curator的框架怎么去封裝的呢* 無非就是先if判斷當前* 如果路徑很長的話* 還有個aaa* 還有個ccc* 一層一層的去判斷* 先判斷children他的上一級有沒有* 再判斷ccc他的上一級有沒有* aaa他的上一級有沒有* 其實就是這個意思* 只要他的上一級沒有我就拋一個異常* 然后像Curator框架* 如果不存在我就一層一層的創建* 如果/testRoot這個不存在就創建出來* 如果/aaa這個不存在就創建出來* /ccc這個不存在我還得創建出來* 他有個ifparentexist的方法* 不允許去創建沒有的父節點的* 咱們把剛才的事說一下* 把剛才的事說一下* 不允許遞歸創建節點* * */
// String ret = zk.create("/testRoot/aaa/ccc/children", "children data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);/*** 如果你這么去創建是不行的* 咱們ls /一下* 我現在根節點下只有一個zookeeper* 然后是沒有testRoot的* 創建它會返回一個什么值呢* 它是拋了一個異常* KeeperException$NoNodeException: KeeperErrorCode = NoNode for /testRoot/children* 在原生的zookeeper* 它是不允許你遞歸的去創建節點的* 以后的Curator框架* 會有一個ifparentexist* 它會幫你把父節點創建好* 這也是原生API的問題* 其實Curator也是把原生的API進行一個封裝* 無非是在判斷節點的時候判斷一下他上個節點是不是存在* 如果不存在就把他創建好* 如果存在的話就創建節點* 這是第一個問題* 在原生的API中是不允許遞歸創建節點的*/
// String ret = zk.create("/testRoot/children", "children data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);/*** create其實是有CallBack和Object的* 就是每個方法都提供了兩套API* 一個是正常的創建同步的創建* 還有個是異步的創建* 異步創建效率有點高* 咱們同步的是一個 * 異步的去刪除* 創建成功了* /testRoot* 然后我在下面ls /* 下面就有testRoot這個節點了* 然后接下來進行delete* 這個Delete操作其實也很簡單* * 這里先注釋* 否則再創建就會報exists節點已經存在* * /testRoot這個創建好了以后* 我再去取的時候就有了* dataVersion = 0* 版本號的標識被改了幾次* update了幾次* * */
// String ret = zk.create("/testRoot", "testRoot11".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// System.out.println(ret);/*** 現在這樣去做* 我這里delete一個節點* 其實它這個delte方法也有* zookeeper能存數據的版本號* 比如我們去get /testRoot* dataVersion = 0* 你會發現這個版本號是0* 這里有很多version* 但是我們就看dataVersion* 第一次是0* 比如我修改一下* set /testRoot 2222* 我再去get的時候發現dataVersion改變了 * dataVersion = 1* 如果我在改一下* set /testRoot 5555* dataVersion = 2* 我能取到每一個版本號* 對應的你要delete哪一個版本號的數據* 你要是寫-1就是全清空* 就是全刪除* 一般我們去做delete節點的話* 現在的testRoot的版本號已經到了2了* * 現在我想去刪除* delete一般傳了第二個參數-1* 4個參數嗎* 然后這里有一個VoidCallback* Object ctx2可以傳入一個Callback的參數* 實例化出來VoidCallback這么一個對象* 這個是一個靜態的* 它是異步的* * */
// zk.delete("/testRoot", -1,new VoidCallback() {
//
// /**
// * 然后你要去重載一個方法是這個processResult
// *
// * 這就是我之前說的一個callback方法
// * 每一個delete都會存在一個回調
// *
// */
// @Override
// public void processResult(int rc, String path, Object ctx2) {
// /**
// * 我休眠1秒鐘
// */
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// /**
// * 第一個int類型的東西是啥啊
// * 響應碼
// * 如果是0表示回調成功
// * 如果是-4表示端口連接
// * 如果是-110表示指定節點存在
// * -112表示會話已經過期
// * callback可能成功可能失敗
// * 能有好多個狀態
// */
// System.out.println(rc);
// /**
// * path表示節點的參數路徑是什么
// *
// * 第二個參數是path
// * 當前刪除的節點是什么
// */
// System.out.println(path);
// /**
// * 然后還有一些東西
// * ctx上下文環境
// * 還有一個name
// * 很多回調的參數
// * 我現在只取了這三個
// *
// */
// System.out.println(ctx2);
// }
//
// /**
// * 這個a是一個Object
// * 就是傳入任何一個參數
// * 都會回調到ctx2這里
// * 咱們之前學RocketMQ的事務
// * 事務的callback里面也會有一個參數
// * 其實一個道理
// * 這是一個callback的一個機制
// * -1是跳過版本檢查
// * 如果不是當前的版本就不刪除了
// * 我數據庫可能有一份記錄
// * 當前咱兩是不是一個版本號
// * 如果是的話我做一個check
// */
// },"a");System.out.println("繼續執行.......");/*** 在這里等待這么長的時間*/
// Thread.sleep(5000);/*** 我現在直接做一個刪除* 成功了以后我們再去get* get /testRoot* Node does not exist: /testRoot* 一般不考慮版本號* 一般用-1* */
// zk.delete("/testRoot", 2);/*** 其實你也不用休眠了*/
// Thread.sleep(10000);//獲取節點洗信息/*** 接下來看獲得節點* ZooKeeper.getData(String path, boolean watch, Stat stat) * watch先不用理會* stat狀態呢也不用理會* 因為如果你要講API的話* 但是不用關注后面兩個參數* 其實就是想去獲得節點的value值* value值返回的還是一個data類型* testRoot咱們包裝成一個String* 基本上就是這個操作* 先講簡單的再講復雜的* 咱們create /testRoot 1122* 創建成功以后ls /* 看到testRoot了* 咱們現在執行一下看有沒有效果* 后面兩個參數我們后面會講* 先不用關注這么復雜的東西* * */
// byte[] data = zk.getData("/testRoot", false, null);
// /**
// * 現在我想獲得一堆怎么辦
// *
// */System.out.println(new String(data));
//
// /**
// * 一堆children我想獲得的話
// *
// */System.out.println(zk.getChildren("/testRoot", false));
//
// /**
// * 他返回的是一個String類型的list
// * ZooKeeper.getChildren(String path, boolean watch)
// * 第二個參數是watch
// * 總是有一個watch的東西
// * 這個東西其實很煩
// * create /testRoot/a1 1111
// * create /testRoot/a2 2222
// * create /testRoot/a5 2222
// * 現在ls /testRoot下面
// * [a1, a2, a5]
// * 你會發現有三個節點三個孩子了
// * 他相當于一個父親了
// * 然后我現在getChildren的時候
// * 把孩子都給拿出來了
// * 這是很簡單的
// * 返回的類型已經固定了
// *
// */
// List<String> list = zk.getChildren("/testRoot", false);
//
// /**
// * 我就去循環他
// *
// */
// for (String path : list) {
// /**
// * 這個path是一個相對的路徑
// * a1,a2,a3
// * 如果你想取值的話
// * 你得加上前面的父路徑/testRoot
// * 然后a1,a2,a3
// * 他取出來的是一個相對的
// * 我就用之前取的方法
// *
// * 我這塊就是這樣去做的
// * 我直接循環path
// * 看path有沒有就行了
// * 你會發現這是沒有的
// * 就是getChildren方法你想做遞歸呢
// * 他是只支持直接子節點
// * 能理解我說的意思吧
// * 這是肯定的
// * 原生API就是這么去實現的
// * 為什么這么去實現呢
// * 其實也是有他的道理的
// * 這是關于getChildren方法的
// *
// *
// */
// System.out.println(path);
// String realPath = "/testRoot/" + path;
// /**
// * realPath這個路徑傳進去
// * 得到的結果就是1111,2222,2222這個了
// * 這個應該很簡單
// * 暫時不用去理會這個事
// * 先把這個簡單的API講完
// * 再加一層就不行了
// * 你試一下
// * 我在a1下面還有一層b1
// * a1下面有一個b1
// * create /testRoot/a1/b1 11
// * 咱們可能就是這種情況了
// * 我現在能不能把b1也取出來呢
// *
// *
// */
// System.out.println(new String(zk.getData(realPath, false, null)));
// }/*** 修改節點其實也很簡單* 修改節點* testRoot原先叫什么名字咱們看一下* get /testRoot* 原先叫1122* 我想給他改成"modify data root"這個* -1就是不檢查版本號了* 不管你版本號這個問題了* 修改完了再去取* 就變成modify data root這個了* get /testRoot* 數據就變成modify data root這個了* 這個就相當于update* * */
// zk.setData("/testRoot", "modify data root".getBytes(), -1);
// byte[] data = zk.getData("/testRoot", false, null);
// System.out.println(new String(data));//修改節點的值
// zk.setData("/testRoot", "modify data root".getBytes(), -1);
// byte[] data = zk.getData("/testRoot", false, null);
// System.out.println(new String(data)); //判斷節點是否存在/*** exists你可以先判斷節點是否已經存在* 比如a2* 打印一下* 它會返回一個布爾類型* 4127,4127,1558515470535,1558515470535,0,0,0,0,4,0,4127* 這個長的字符串不用理會* 就跟cZxid = 0x101b這個有點像了* 來試一下吧* 每次都一樣* 我不知道行不行* * 如果你給一個a5* */
// System.out.println(zk.exists("/testRoot/a1", false));/*** 如果你給一個a8* 我在運行的話* 返回的就是一個Null* null就證明這個節點不存在* 如何去判斷是否存在* 現在也把exist這個方法測試OK了* 然后最后還有一個delete* 這個delete就不用說了* 就是想刪除一個節點* 既然不支持遞歸的創建* * get /testRoot* 我現在查的是a2* 那我還得這么去做* get /testRoot/a2* cZxid = 0x1035* a2是這個值也應該存在* * * */System.out.println(zk.exists("/testRoot/a2", false));long k = 0x1035;System.out.println(k);// 刪除節點/*** 原生的API只有兩個方法* 基本上你找不到遞歸刪除的方法* * */
// zk.delete("/testRoot/a2", -1);/*** 刪除這個就報異常了* KeeperException$NotEmptyException: KeeperErrorCode = Directory not empty for /testRoot* Directory not empty* 這個Directory不是空的* 下面有孩子你就刪不了了* 既不支持遞歸的創建* 也不支持遞歸的刪除* 能理解這個意思嗎* 這是最基本的API操作* 讓你去了解zookeeper最基本的操作* * */
// zk.delete("/testRoot", -1);
// System.out.println(zk.exists("/testRoot/a2", false));zk.close();}}
?
總結
以上是生活随笔為你收集整理的Zookeeper_原生API操作(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Zookeeper_原生API操作(一)
- 下一篇: Zookeeper_watch机制核心讲