znode是Zookeeper集合的核心組件,Zookeeper api提供了一小組方法使用Zookeeper集合來操縱znode的所有細節。這裏沒有涉及watch相關的api,另外介紹。
客戶端應該遵循以下步驟,以保證與Zookeeper服務器進行清晰乾淨的交互。
- 連接到Zookeeper服務器。Zookeeper服務器爲客戶端分配會話ID。
- 定期向服務器發送心跳。否則Zookeeper服務器將過期會話ID。客戶端需要重新連接。
- 只要會話ID處於活躍狀態,就可以操作znode。
- 所以任務完成後,斷開與Zookeeper服務器的連接。如果客戶端長期不活動,則Zookeeper服務器將自動斷開客戶端。
需要導入依賴
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
demo代碼,每種操作都要同步異步兩種模式。
public class ZookeeperDemo implements Watcher{
private ZooKeeper zooKeeper;
//路徑前綴
private static final String PATH_PREFIX = "/javaapi";
//相當於發令槍,會阻塞線程,待得Count變爲0時,就喚醒線程。
CountDownLatch latch = new CountDownLatch(1);
@Override
public void process(WatchedEvent watchedEvent) {
if (watchedEvent.getState().equals(Event.KeeperState.SyncConnected)){
//如果客戶端已經連接到Zookeeper服務器,就會觸發SyncConnected事件,進入這裏。
//發令器減1 變爲0 被該發令器阻塞的線程將被喚醒
latch.countDown();
System.out.println("已連接到客戶端");
}
if (watchedEvent.getState().equals(Event.KeeperState.Expired)){
//latch.countDown();
System.out.println("連接超時");
}
}
@Before
public void init(){
try {
//創建一個Zookeeper客戶端並連接到服務器,因爲連接過程是異步執行的,所以要配合CountDownLatch保證在發送請求前客戶端
//與服務器已經建立了連接
//第一個參數:要連接到Zookeeper服務器地址。 第二個參數,session超時,單位毫秒。 第三個參數,爲該客戶端註冊監聽。
zooKeeper = new ZooKeeper("192.168.18.130:2181",5000,this);
//阻塞線程。CountDownLatch減爲0時就會喚醒線程
latch.await();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@After
public void destroy(){
try {
//關閉連接
zooKeeper.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 同步創建節點
*/
@Test
public void testAddNodeSync(){
try {
//第一個參數 創建節點的路徑
//第二個參數 節點的數據 ,是一個字節數組
//第三個參數 節點的ACL list列表 ZooDefs.Ids.READ_ACL_UNSAFE代表 world:anyone:adcwr ,後面會詳解。
//第四個參數 節點的類型
//返回值是創建的節點的路徑
//創建持久化節點
String result = zooKeeper.create(PATH_PREFIX + "/PersistentNode","PersistentNode".getBytes(),
ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(result);
//創建持久化有序節點
zooKeeper.create(PATH_PREFIX + "/PersistentSeqNode","PersistentSeqNode".getBytes(),
ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.PERSISTENT);
//創建臨時節點
zooKeeper.create(PATH_PREFIX + "/EPHEMERALNode","EPHEMERALNode".getBytes(),
ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.EPHEMERAL);
//創建臨時有序節點
zooKeeper.create(PATH_PREFIX + "/EPHEMERALSeqNode","EPHEMERALSeqNode".getBytes(),
ZooDefs.Ids.READ_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 異步創建節點
*/
@Test
public void testAddNodeAsync(){
//第一個參數 創建節點的路徑
//第二個參數 節點的數據 ,是一個字節數組
//第三個參數 節點的ACL ZooDefs.Ids.READ_ACL_UNSAFE代表 world:anyone:adcwr ,後面會詳解。
//第四個參數 節點的類型
//第五個參數 節點創建完成(無論成功還是失敗)的回調。
//上下文ctx
zooKeeper.create(PATH_PREFIX + "/asyncNode", "asyncNode".getBytes(), ZooDefs.Ids.READ_ACL_UNSAFE,
CreateMode.PERSISTENT, new AsyncCallback.StringCallback() {
@Override
public void processResult(int rc, String path, Object ctx, String name) {
//結果標誌,爲0時代表節點創建成功
System.out.println(rc);
//節點的路徑
System.out.println(path);
//上下文參數,就是該create方法傳入的最後一個參數
System.out.println(ctx);
//節點的路徑。當創建節點失敗時,爲null。
System.out.println(name);
}
},"aa");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//創建節點時賦予特定的ACL
@Test
public void testCreateNodeWithACL(){
//創建ACL訪問控制列表
//第一個參數是權限 1代表 read 2 代表 write 4代表 create 8代表 delete 16代表 admin 相互組合用相加 比如1+2=3 是rw
//第二個參數 Id對象代表授權模型和授權對象 ,構造函數第一個是授權模型 world、ip、auth、digest 第二個參數是授權對象
//world:anyone:r
ACL aclWorld = new ACL(1,new Id("world","anyone"));
//digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:adcwr
ACL aclDigest= new ACL(1+2+4+8+16,new Id("digest","admin:0uek/hZ/V9fgiM35b0Z2226acMQ="));
//ip:192.168.18.128:rw
ACL aclIP = new ACL(1+2,new Id("ip","192.168.18.128"));
//把ACL添加到acl列表中,這個就是創建節點要傳遞的參數
List<ACL> acls = new ArrayList<>();
acls.add(aclWorld);
acls.add(aclDigest);
acls.add(aclIP);
try {
zooKeeper.create(PATH_PREFIX + "/nodeACL","nodeACL".getBytes(),
acls,CreateMode.PERSISTENT);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//同步修改節點數據
@Test
public void testSetNode(){
try {
//第一個參數 修改的節點路徑
//第二個參數 修改的節點數據
//數據的版本號 ,用於實現樂觀鎖的,如果此版本號與Zookeeper服務器中的該節點數據的版本號不一樣,就會修改失敗
//返回的是修改後節點的元信息,用Stat類封裝,與命令 stat path 返回的一致。-1表示該參數不參與修改節點
Stat stat = zooKeeper.setData(PATH_PREFIX + "/nodeSetData","nodeSetData".getBytes(),-1);
System.out.println(stat.getVersion());
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//修改節點ACL
@Test
public void testSetNodeACL(){
ACL aclWorld = new ACL(1,new Id("world","anyone"));
//digest:admin:0uek/hZ/V9fgiM35b0Z2226acMQ=:adcwr
ACL aclDigest= new ACL(1+2+4+8+16,new Id("digest","admin:0uek/hZ/V9fgiM35b0Z2226acMQ="));
//ip:192.168.18.128:rw
ACL aclIP = new ACL(1+2,new Id("ip","192.168.18.128"));
List<ACL> acls = new ArrayList<>();
acls.add(aclWorld);
acls.add(aclDigest);
acls.add(aclIP);
try {
//第一個參數,節點路徑
//第二個參數:要修改的ACL
//第三個參數aVersion -1則表示該參數不參與
zooKeeper.setACL(PATH_PREFIX + "/setACL",acls,-1);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//刪除節點
@Test
public void testDeleteNode(){
try {
//第一個參數:要刪除的節點路徑
//第二個參數數據版本,-1表示不參與。
zooKeeper.delete(PATH_PREFIX + "/nodeDelete",-1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
//查看節點數據
@Test
public void getData(){
Stat stat = new Stat();
try {
//第一個參數是節點路徑
//第二個參數是是否註冊監聽,true的話就會註冊創建Zookeeper客戶端時傳入的監聽,第三個是傳入stat對象,會把獲取到的節點元信息賦值到該對象中。
//返回值是節點數據的字節數組
byte[] result = zooKeeper.getData(PATH_PREFIX + "/nodeSetData",false,stat);
System.out.println(new String(result,"utf-8"));
System.out.println(stat.getVersion());
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
//獲取子節點
@Test
public void getChildrenNode(){
try {
//第一個參數:節點路徑
//第二個參數:是否註冊監聽
//返回值 節點路徑列表list。
List<String> children = zooKeeper.getChildren(PATH_PREFIX, false);
System.out.println(children);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//查看節點是否存在
@Test
public void testNodeExist(){
try {
//參數1:節點路徑
//第二個參數:是否註冊監聽
//返回值 stat對象,節點的元數據,如果節點不存在就返回null
Stat exists = zooKeeper.exists("/888", false);
System.out.println(exists);
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}