一、docker安裝zookeeper
1.拉取鏡像。默認是最新版本
docker pull zookeeper
2.運行docker鏡像
docker run --privileged=true -d --name zookeeper --publish 2181:2181 -d zookeeper:latest
3.查看容器
docker ps
4.(1)idea zookeeper 查看工具
(2)zkclient
二.Zookeeper工具類編寫(引用curator連接方式)
curator的詳解,請參考:https://www.jianshu.com/p/70151fc0ef5d
1.工具類
package com.utils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.ACLBackgroundPathAndBytesable;
import org.apache.curator.framework.api.BackgroundPathAndBytesable;
import org.apache.curator.framework.api.BackgroundPathable;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.framework.recipes.locks.InterProcessLock;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Date: 2018年11月6日
*
* @author lk
*/
public class ZkClient {
private String zkAddr;
private int timeOut;
private String authSchema;
private String authInfo;
private CuratorFramework client;
public ZkClient(String zkAddr, int timeOut, String namespace) throws Exception{
this(zkAddr,timeOut,namespace,null);
}
/**
* 獲取zk 連接客戶端
* @param zkAddr zk地址 ip:port,ip:port,ip:port
* @param timeOut 連接超時ms
* @param namespace 所有的操作都是在 /namespace 下的節點操作
* @param acl Access Control List(訪問控制列表)。Znode被創建時帶有一個ACL列表<br>
* acl 主要由三個維度:schema,id,permision 控制節點權限 <br>
* eg:<br>
* Id id = new Id("digest", DigestAuthenticationProvider.generateDigest("username:password"));<br>
* ACL acl = new ACL(ZooDefs.Perms.ALL, id); <br>
* <br>
* 維度 schema: <br>
* 1:digest 用戶名+密碼驗證 它對應的維度id=username:BASE64(SHA1(password))<br>
* 2:host 客戶端主機名hostname驗證 <br>
* 3:ip 它對應的維度id=客戶機的IP地址,設置的時候可以設置一個ip段,比如ip:192.168.1.0/16, 表示匹配前16個bit的IP段<br>
* 4:auth 使用sessionID驗證 <br>
* 5:world 無驗證,默認是無任何權限 它下面只有一個id, 叫anyone <br>
* 6:super: 在這種scheme情況下,對應的id擁有超級權限,可以做任何事情(cdrwa) <br>
* 7:sasl: sasl的對應的id,是一個通過了kerberos認證的用戶id <br>
* <br>
* 維度:permision <br>
* ZooDefs.Perms.READ 讀權限<br>
* ZooDefs.Perms.WRITE 寫權限<br>
* ZooDefs.Perms.CREATE 創建節點權限<br>
* ZooDefs.Perms.DELETE 刪除節點權限<br>
* ZooDefs.Perms.ADMIN 能設置權限<br>
* ZooDefs.Perms.ALL 所有權限<br>
* ALL = READ | WRITE | CREATE | DELETE | ADMIN<br>
* @throws Exception
*/
public ZkClient(String zkAddr, int timeOut, String namespace,ACL acl) throws Exception{
this.zkAddr = zkAddr;
if (timeOut > 0) {
this.timeOut = timeOut;
}
if (null != acl) {
this.authSchema = acl.getId().getScheme();
this.authInfo = acl.getId().getId();
}
CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory
.builder().connectString(this.zkAddr).namespace(StringUtils.isEmpty(namespace)?"":namespace)
.connectionTimeoutMs(this.timeOut)
.retryPolicy(new RetryNTimes(5, 10));
if ((!StringUtils.isEmpty(this.authSchema))
&& (!StringUtils.isEmpty(this.authInfo))) {
builder.authorization(this.authSchema, this.authInfo.getBytes());
}
this.client = builder.build();
this.client.start();
this.client.blockUntilConnected(5, TimeUnit.SECONDS);
}
/**
* 創建一個所有權限節點即schema:world;id:annyone;permision:ZooDefs.Perms.ALL
* @param nodePath 創建的結點路徑
* @param data 節點數據
* @param createMode 節點模式
* @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
* @throws Exception
*/
public void createNode(String nodePath, String data, CreateMode createMode,boolean recursion)
throws Exception {
createNode(nodePath, ZooDefs.Ids.OPEN_ACL_UNSAFE, data, createMode,recursion);
}
/**
* 創建節點
* @param nodePath 創建節點的路徑
* @param acls 節點控制權限列表
* @param data 節點存放的數據
* @param createMode 創建節點的模式
* @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
* 節點模式CreateMode<br>
* 1:CreateMode.EPHEMERAL 創建臨時節點;該節點在客戶端掉線的時候被刪除<br>
* 2:CreateMode.EPHEMERAL_SEQUENTIAL 臨時自動編號節點,一旦創建這個節點的客戶端與服務器端口也就是session 超時,這種節點會被自動刪除,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點(可做分佈式鎖)<br>
* 3:CreateMode.PERSISTENT 持久化目錄節點,存儲的數據不會丟失。<br>
* 4:CreateMode.PERSISTENT_SEQUENTIAL 順序自動編號的持久化目錄節點,存儲的數據不會丟失,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點名<br>
* @throws Exception
*/
public void createNode(String nodePath, List<ACL> acls, String data,
CreateMode createMode,boolean recursion) throws Exception {
byte[] bytes = null;
if (!StringUtils.isEmpty(data)) {
bytes = data.getBytes("UTF-8");
}
createNode(nodePath, acls, bytes, createMode,recursion);
}
/**
* @param nodePath 創建節點的路徑
* @param acls 節點控制權限列表
* @param data 節點存放的數據
* @param createMode 創建節點的模式
* @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
* 節點模式CreateMode<br>
* 1:CreateMode.EPHEMERAL 創建臨時節點;該節點在客戶端掉線的時候被刪除<br>
* 2:CreateMode.EPHEMERAL_SEQUENTIAL 臨時自動編號節點,一旦創建這個節點的客戶端與服務器端口也就是session 超時,這種節點會被自動刪除,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點(可做分佈式鎖)<br>
* 3:CreateMode.PERSISTENT 持久化目錄節點,存儲的數據不會丟失。<br>
* 4:CreateMode.PERSISTENT_SEQUENTIAL 順序自動編號的持久化目錄節點,存儲的數據不會丟失,並且根據當前已近存在的節點數自動加 1,然後返回給客戶端已經成功創建的目錄節點名<br>
* @throws Exception
*/
public void createNode(String nodePath, List<ACL> acls, byte[] data,
CreateMode createMode,boolean recursion) throws Exception {
if(recursion){
((BackgroundPathAndBytesable<?>) ((ACLBackgroundPathAndBytesable<?>) this.client
.create().creatingParentsIfNeeded().withMode(createMode))
.withACL(acls)).forPath(nodePath, data);
}
else{
((BackgroundPathAndBytesable<?>) ((ACLBackgroundPathAndBytesable<?>) this.client
.create().withMode(createMode))
.withACL(acls)).forPath(nodePath, data);
}
}
/**
* 創建一個所有權限的永久節點
* @param nodePath
* @param data
* @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
* @throws Exception
*/
public void createPersitentNode(String nodePath, String data,boolean recursion) throws Exception {
createNode(nodePath, data, CreateMode.PERSISTENT,recursion);
}
/**
* 創建一個所有權限的零時節點
* @param nodePath
* @param data
* @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
* @throws Exception
*/
public void createEphemeralNode(String nodePath, String data,boolean recursion) throws Exception {
createNode(nodePath, data, CreateMode.EPHEMERAL,recursion);
}
/**
* 創建一個帶權限的永久節點
* @param nodePath
* @param data
* @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
* @throws Exception
*/
public void createPersitentNodeWithAcl(String nodePath, String data,List<ACL> acls,boolean recursion) throws Exception {
createNode(nodePath, acls, data, CreateMode.PERSISTENT,recursion);
}
/**
* 創建一個帶權限的零時節點
* @param nodePath
* @param data
* @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
* @throws Exception
*/
public void createEphemeralNodeAcl(String nodePath, String data,List<ACL> acls,boolean recursion) throws Exception {
createNode(nodePath, acls, data, CreateMode.EPHEMERAL,recursion);
}
/**
* 創建序列節點且當父節點不存在時創建父節點
* @param nodePath
* @param acls 可參考:ZooDefs.Ids
* @param createMode
* @param recursion 當父目錄不存在是否創建 true:創建,fasle:不創建
* @throws Exception
*/
public void createSeqNode(String nodePath,List<ACL> acls,CreateMode createMode,boolean recursion) throws Exception {
if(recursion){
((BackgroundPathAndBytesable<?>) ((ACLBackgroundPathAndBytesable<?>) this.client
.create().creatingParentsIfNeeded()
.withMode(createMode))
.withACL(acls)).forPath(nodePath);
}
else{
((BackgroundPathAndBytesable<?>) ((ACLBackgroundPathAndBytesable<?>) this.client
.create()
.withMode(createMode))
.withACL(acls)).forPath(nodePath);
}
}
/**
* 存在返回 節點stat 信息;否則返回null
* @param path
* @return
* @throws Exception
*/
public Stat exists(String path) throws Exception {
return this.client.checkExists().forPath(path);
}
/**
* 判斷節點是否存在,存在則註冊節點監視器
* @param path
* @param watcher
* @return
*/
public boolean exists(String path, Watcher watcher) throws Exception {
if (null != watcher) {
return null != ((BackgroundPathable<?>) this.client.checkExists().usingWatcher(watcher)).forPath(path);
}
return null != this.client.checkExists().forPath(path);
}
/**
* 判斷是否處於連接狀態
* @return
*/
public boolean isConnected() {
if ((null == this.client)
|| (!CuratorFrameworkState.STARTED.equals(this.client
.getState()))) {
return false;
}
return true;
}
public void retryConnection() {
this.client.start();
}
/**
* 獲取連接客戶端
* @return
*/
public CuratorFramework getInnerClient(){
return this.client;
}
/**
* 關閉連接
*/
public void quit() {
if ((null != this.client)
&& (CuratorFrameworkState.STARTED
.equals(this.client.getState()))) {
this.client.close();
}
}
/**
* 刪除節點
* @param path
* @param deleChildren
* @throws Exception
*/
public void deleteNode(String path,boolean deleChildren) throws Exception {
if(deleChildren){
this.client.delete().guaranteed().deletingChildrenIfNeeded()
.forPath(path);
}
else{
this.client.delete().forPath(path);
}
}
/**
* 設置節點數據
* @param nodePath
* @param data
* @throws Exception
*/
public void setNodeData(String nodePath, String data) throws Exception {
byte[] bytes = null;
if (!StringUtils.isEmpty(data)) {
bytes = data.getBytes("UTF-8");
}
setNodeData(nodePath, bytes);
}
/**
* 設置節點數據
* @param nodePath
* @param data
* @throws Exception
*/
public void setNodeData(String nodePath, byte[] data) throws Exception {
this.client.setData().forPath(nodePath, data);
}
public String getNodeData(String nodePath, boolean watch) throws Exception {
byte[] data;
if (watch) {
data = (byte[]) ((BackgroundPathable<?>) this.client.getData()
.watched()).forPath(nodePath);
} else {
data = (byte[]) this.client.getData().forPath(nodePath);
}
if ((null == data) || (data.length <= 0)) {
return null;
}
return new String(data, "UTF-8");
}
public String getNodeData(String nodePath) throws Exception {
return getNodeData(nodePath, false);
}
public String getNodeData(String nodePath, Watcher watcher)
throws Exception {
byte[] data = getNodeBytes(nodePath, watcher);
return new String(data, "UTF-8");
}
public byte[] getNodeBytes(String nodePath, Watcher watcher)
throws Exception {
byte[] bytes = null;
if (null != watcher) {
bytes = (byte[]) ((BackgroundPathable<?>) this.client.getData()
.usingWatcher(watcher)).forPath(nodePath);
} else {
bytes = (byte[]) this.client.getData().forPath(nodePath);
}
return bytes;
}
public byte[] getNodeBytes(String nodePath) throws Exception {
return getNodeBytes(nodePath, null);
}
@SuppressWarnings("unchecked")
public List<String> getChildren(String nodePath, Watcher watcher)
throws Exception {
return (List<String>) ((BackgroundPathable<?>) this.client
.getChildren().usingWatcher(watcher)).forPath(nodePath);
}
public List<String> getChildren(String path) throws Exception {
return (List<String>) this.client.getChildren().forPath(path);
}
@SuppressWarnings("unchecked")
public List<String> getChildren(String path, boolean watcher)
throws Exception {
if (watcher) {
return (List<String>) ((BackgroundPathable<?>) this.client
.getChildren().watched()).forPath(path);
}
return (List<String>) this.client.getChildren().forPath(path);
}
public ZkClient addAuth(String authSchema, String authInfo)
throws Exception {
synchronized (ZkClient.class) {
this.client.getZookeeperClient().getZooKeeper()
.addAuthInfo(authSchema, authInfo.getBytes());
}
return this;
}
/**
* 分佈式鎖
* @param lockPath
* @return
*/
public InterProcessLock getInterProcessLock(String lockPath) {
return new InterProcessMutex(this.client, lockPath);
}
}
/***
* 監聽節點變化
*/
public PathChildrenCache watchNode(String path,PathChildrenCacheListener listener) throws Exception {
//創建監聽
PathChildrenCache pathChildrenCache=new PathChildrenCache(this.client,path,true);
pathChildrenCache.getListenable().addListener(listener);
pathChildrenCache.start();
return pathChildrenCache;
}
2.pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mavenzookeeper</groupId>
<artifactId>mavenzookeeper</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mavenzookeeper</name>
<packaging>jar</packaging>
<!-- 繼承父包 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.3.RELEASE</version>
<relativePath></relativePath>
</parent>
<!-- spring-boot的web啓動的jar包 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--jpa的jar包 ,操作數據庫的,類似hibernate-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--thymeleaf模板jar,是很不錯的html數據傳遞取值,類似jsp的jstl-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
</dependencies>
<!--maven的插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!-- 配置java版本 不配置的話默認父類配置的是1.6-->
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
3.監聽最後調用和分佈式鎖測試
package com.controller;
import com.utils.ZkClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/zkController")
public class getZK {
@Autowired
private ZkClientService zkClient;
@RequestMapping("/getZK")
@ResponseBody
public String getZK(){
try {
List<String> children = zkClient.getChildren("/");
System.out.println(children.stream().toString());
//分佈式鎖測試
for(int i=0;i<50;i++){
new Thread(()->{
InterProcessLock lock = null;
try{
lock = zkClient.getInterProcessLock("/distributeLock");
System.out.println(Thread.currentThread().getName()+"申請鎖");
lock.acquire();
System.out.println(Thread.currentThread().getName()+"持有鎖");
Thread.sleep(500);
}
catch(Exception e){
e.printStackTrace();
}
finally{
if(null != lock){
try {
lock.release();
System.out.println(Thread.currentThread().getName()+"釋放有鎖");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
zkClient.watchNode("/",(client,event)->{//對節點進行監聽
String data=new String(event.getData().getData());
switch (event.getType()){
case CHILD_ADDED: {
System.out.println("Node added: " + data);
break;
}
case CHILD_UPDATED: {
System.out.println("Node changed: " + data);
break;
}
case CHILD_REMOVED: {
System.out.println("Node removed: " + data);
break;
}
}
});
return "ok";
}catch (Exception e){
e.printStackTrace();
return "error";
}
}
}