一、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列表
* acl 主要由三个维度:schema,id,permision 控制节点权限
* eg:
* Id id = new Id("digest", DigestAuthenticationProvider.generateDigest("username:password"));
* ACL acl = new ACL(ZooDefs.Perms.ALL, id);
*
* 维度 schema:
* 1:digest 用户名+密码验证 它对应的维度id=username:BASE64(SHA1(password))
* 2:host 客户端主机名hostname验证
* 3:ip 它对应的维度id=客户机的IP地址,设置的时候可以设置一个ip段,比如ip:192.168.1.0/16, 表示匹配前16个bit的IP段
* 4:auth 使用sessionID验证
* 5:world 无验证,默认是无任何权限 它下面只有一个id, 叫anyone
* 6:super: 在这种scheme情况下,对应的id拥有超级权限,可以做任何事情(cdrwa)
* 7:sasl: sasl的对应的id,是一个通过了kerberos认证的用户id
*
* 维度:permision
* ZooDefs.Perms.READ 读权限
* ZooDefs.Perms.WRITE 写权限
* ZooDefs.Perms.CREATE 创建节点权限
* ZooDefs.Perms.DELETE 删除节点权限
* ZooDefs.Perms.ADMIN 能设置权限
* ZooDefs.Perms.ALL 所有权限
* ALL = READ | WRITE | CREATE | DELETE | ADMIN
* @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
* 1:CreateMode.EPHEMERAL 创建临时节点;该节点在客户端掉线的时候被删除
* 2:CreateMode.EPHEMERAL_SEQUENTIAL 临时自动编号节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点(可做分布式锁)
* 3:CreateMode.PERSISTENT 持久化目录节点,存储的数据不会丢失。
* 4:CreateMode.PERSISTENT_SEQUENTIAL 顺序自动编号的持久化目录节点,存储的数据不会丢失,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名
* @throws Exception
*/
public void createNode(String nodePath, List 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
* 1:CreateMode.EPHEMERAL 创建临时节点;该节点在客户端掉线的时候被删除
* 2:CreateMode.EPHEMERAL_SEQUENTIAL 临时自动编号节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点(可做分布式锁)
* 3:CreateMode.PERSISTENT 持久化目录节点,存储的数据不会丢失。
* 4:CreateMode.PERSISTENT_SEQUENTIAL 顺序自动编号的持久化目录节点,存储的数据不会丢失,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名
* @throws Exception
*/
public void createNode(String nodePath, List 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 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 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 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 getChildren(String nodePath, Watcher watcher)
throws Exception {
return (List) ((BackgroundPathable>) this.client
.getChildren().usingWatcher(watcher)).forPath(nodePath);
}
public List getChildren(String path) throws Exception {
return (List) this.client.getChildren().forPath(path);
}
@SuppressWarnings("unchecked")
public List getChildren(String path, boolean watcher)
throws Exception {
if (watcher) {
return (List) ((BackgroundPathable>) this.client
.getChildren().watched()).forPath(path);
}
return (List) 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文件
4.0.0
mavenzookeeper
mavenzookeeper
0.0.1-SNAPSHOT
mavenzookeeper
jar
org.springframework.boot
spring-boot-starter-parent
1.1.3.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-jpa
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-thymeleaf
org.apache.zookeeper
zookeeper
3.4.6
pom
org.apache.curator
curator-recipes
2.12.0
org.apache.curator
curator-framework
2.12.0
org.springframework.boot
spring-boot-maven-plugin
maven-compiler-plugin
1.8
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 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";
}
}
}