创建一个maven项目导入zookeeper的坐标即可
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.5.7version>
dependency>
我是用了Junit5的测试
//表示连接的Zookeeper地址端口,,两端不能有空格
private String connect = "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181";
//还要这里面不能直接使用ip地址:需要到windows的C:\Windows\System32\drivers\etc\hosts文件下配置地址映射
//否则就会出现连接不上的异常
//设置超时时间
private Integer timeout = 2000;
private ZooKeeper zkClient = null;
@BeforeEach//Junit5注解表示在测试方法执行前都执行
public void init() throws IOException {
zkClient = new ZooKeeper(connect, timeout, new Watcher() {//Watcher : 监听函数
@Override
public void process(WatchedEvent event) {
//监听代码
}
});
}
测试代码
//创建节点方法
@Test
public void createNode() throws Exception {
String createNode = zkClient.create("/mnyuan", //参数1:要创建节点的路径
"java".getBytes(), //参数2:节点数据
ZooDefs.Ids.OPEN_ACL_UNSAFE, //参数3:节点权限
CreateMode.PERSISTENT);//参数4:节点的类型
System.out.println(createNode);
}
在本机上查看节点
[zk: localhost:2181(CONNECTED) 0] ls /
[mnyuan, zookeeper]
使用之前的监听
@BeforeEach//Junit5注解表示在测试方法执行前都执行
public void init() throws IOException {
zkClient = new ZooKeeper(connect, timeout, new Watcher() {//Watcher : 监听函数
@Override
public void process(WatchedEvent event) {
System.out.println("-------------------");
try {
List<String> children = zkClient.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------------------");
}
});
}
监听测试
//监听子节点
@Test
public void watchNode() throws KeeperException, InterruptedException {
List<String> children = zkClient.getChildren("/", true);
//使用true表示使用的之前创建的监听,参数二也可以自己编写一个监听
for (String child : children) {
System.out.println(child);
}
Thread.sleep(15000);//让该方法延迟一会,方法不能太早结束,
}
//判断节点是否存在
@Test
public void exitsNode() throws KeeperException, InterruptedException {
Stat stat = zkClient.exists("/mnyuan", false);//false : 不监听
System.out.println(stat == null ? "node not exists" : "yes");
}
1、现在集群上创建/servers节点
[zk: localhost:2181(CONNECTED) 4] create /servers
Created /servers
2、服务器端向Zookeeper注册的代码
public class ZookeeperServer {
private String connect = "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181";
private int timeOut = 2000;
private ZooKeeper zK = null;
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZookeeperServer server = new ZookeeperServer();
//1. 获取连接
server.connect();
//2.注册服务器
server.registServer(args[0]);
//3.业务功能
server.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void registServer(String hostName) throws KeeperException, InterruptedException {
String servers = zK.create("/servers/" + hostName, hostName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);
System.out.println(servers + "on line");
}
private void connect() throws IOException {
zK = new ZooKeeper(connect, timeOut, new Watcher() {
@Override
public void process(WatchedEvent event) {
}
});
}
}
3、进行测试
使用main方法传递参数,注意注意观察控制台参数
2、服务器上下线,客户端监听的代码
public class ZookeeperClient {
private String connect = "zookeeper01:2181,zookeeper02:2181,zookeeper03:2181";
private int timeOut = 2000;
private ZooKeeper zk = null;
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
ZookeeperClient client = new ZookeeperClient();
//1.获取连接
client.connect();
//2.获取服务器列表信息
client.getServerList();
//3.业务逻辑
client.business();
}
private void business() throws InterruptedException {
Thread.sleep(Long.MAX_VALUE);
}
private void getServerList() throws KeeperException, InterruptedException {
//监听/servers节点
List<String> children = zk.getChildren("/servers", true);
//servers用来保存节点信息
List<String> servers = new ArrayList<>();
for (String child : children) {
//获取节点信息
byte[] data = zk.getData("/servers/" + child, false, null);
if (data != null){
servers.add(data.toString());
}
}
//打印节点列表
System.out.println(servers);
}
private void connect() throws IOException {
zk = new ZooKeeper(connect, timeOut, new Watcher() {
@Override
public void process(WatchedEvent event) {
try {
//只有再次调用才能监听
getServerList();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
3、测试,注意观察控制台打印情况
[zk: localhost:2181(CONNECTED) 13] create -e -s /servers/zookeeper01 "zookeeper01"
Created /servers/zookeeper010000000001
[zk: localhost:2181(CONNECTED) 14] create -e -s /servers/zookeeper02 "zookeeper02"
Created /servers/zookeeper020000000002
[zk: localhost:2181(CONNECTED) 15] delete /servers/zookeeper010000000001
原生代码实现
public class DistributedLock {
private final String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private final int sessionTimeout = 2000;
private final ZooKeeper zk;
private CountDownLatch connectLatch = new CountDownLatch(1);
private CountDownLatch waitLatch = new CountDownLatch(1);
private String waitPath;
private String currentMode;
public DistributedLock() throws IOException, InterruptedException, KeeperException {
// 获取连接
zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
// connectLatch 如果连接上zk 可以释放
if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
connectLatch.countDown();
}
// waitLatch 需要释放
if (watchedEvent.getType()== Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)){
waitLatch.countDown();
}
}
});
// 等待zk正常连接后,往下走程序
connectLatch.await();
// 判断根节点/locks是否存在
Stat stat = zk.exists("/locks", false);
if (stat == null) {
// 创建一下根节点
zk.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
}
// 对zk加锁
public void zklock() {
// 创建对应的临时带序号节点
try {
currentMode = zk.create("/locks/" + "seq-", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
// wait一小会, 让结果更清晰一些
Thread.sleep(10);
// 判断创建的节点是否是最小的序号节点,如果是获取到锁;如果不是,监听他序号前一个节点
List<String> children = zk.getChildren("/locks", false);
// 如果children 只有一个值,那就直接获取锁; 如果有多个节点,需要判断,谁最小
if (children.size() == 1) {
return;
} else {
Collections.sort(children);
// 获取节点名称 seq-00000000
String thisNode = currentMode.substring("/locks/".length());
// 通过seq-00000000获取该节点在children集合的位置
int index = children.indexOf(thisNode);
// 判断
if (index == -1) {
System.out.println("数据异常");
} else if (index == 0) {
// 就一个节点,可以获取锁了
return;
} else {
// 需要监听 他前一个节点变化
waitPath = "/locks/" + children.get(index - 1);
zk.getData(waitPath,true,new Stat());
// 等待监听
waitLatch.await();
return;
}
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 解锁
public void unZkLock() {
// 删除节点
try {
zk.delete(this.currentMode,-1);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (KeeperException e) {
e.printStackTrace();
}
}
}
测试
public class DistributedLockTest {
public static void main(String[] args) throws InterruptedException, IOException, KeeperException {
final DistributedLock lock1 = new DistributedLock();
final DistributedLock lock2 = new DistributedLock();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock1.zklock();
System.out.println("线程1 启动,获取到锁");
Thread.sleep(5 * 1000);
lock1.unZkLock();
System.out.println("线程1 释放锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock2.zklock();
System.out.println("线程2 启动,获取到锁");
Thread.sleep(5 * 1000);
lock2.unZkLock();
System.out.println("线程2 释放锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
1》原生的javaAPI开发存在的问题
2》Curator是一个专门解决分布式锁的框架,解决原生javaAPI开发分布式遇到的问题
官方文档 :Apache Curator
使用演示
1、导入maven坐标
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>4.3.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>4.3.0version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-clientartifactId>
<version>4.3.0version>
dependency>
2、编写测试
public class ZookeeperLocks {
public static void main(String[] args) {
//创建分布式锁1 : lock1
InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");
//创建分布式锁2 : lock2
InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");
new Thread(new Runnable() {
@Override
public void run() {
try {
lock1.acquire();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程1 获取到锁");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
lock2.acquire();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程2 获取到锁");
}
}).start();
}
public static CuratorFramework getCuratorFramework(){
ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3);
CuratorFramework client = CuratorFrameworkFactory
.builder()
.connectString("zookeeper01:2181,zookeeper02:2182,zookeeper03:2181")
.connectionTimeoutMs(2000)
.sessionTimeoutMs(2000)
.retryPolicy(policy).build();
client.start();//启动客户端
System.out.println("Zookeeper启动成功");
return client;
}
}