Java-第十九部分-Zookeeper-API、动态上下线和分布式锁

143 阅读2分钟

Zookeeper全文

API

依赖

// 测试
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>RELEASE</version>
</dependency>
// log
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.8.2</version>
</dependency>
// zookeeper
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.5.9</version>
</dependency>
// zk框架
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.3.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.3.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-client</artifactId>
    <version>4.3.0</version>
</dependency>

方法

  • 创建客户端
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
    @Override
    //处理监听事件
    public void process(WatchedEvent watchedEvent) {
        try {
            if (watchedEvent.getType() == Event.EventType.NodeChildrenChanged) {
                List<String> children = zkClient.getChildren("/", true);
                System.out.println(children);
            } else {
                System.out.println(watchedEvent);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});
  • 创建节点
String res = zkClient.create("/api", "test.api".getBytes(StandardCharsets.UTF_8),
        ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
  • 获取子节点,并且监听为true,就是用创建时的监听方法
List<String> children = zkClient.getChildren("/", true);
  • 判断某一个节点是否存在,不存在返回null,存在返回节点信息
Stat exists = zkClient.exists("/api", false);
  • 读取数据、设置数据,都需要版本号
Stat exists = zkClient.exists("/api", false);
byte[] data = zkClient.getData("/api", false, exists);
System.out.println(exists);
System.out.println(new String(data));
Stat stat = zkClient.setData("/api", "hello".getBytes(StandardCharsets.UTF_8), exists.getVersion());
data = zkClient.getData("/api", false, stat);
System.out.println(stat);
System.out.println(new String(data));

动态上下线

  • 服务端,关闭服务后,会移除临时创建的节点
public class DistributeServer {

    private String connectString = "localhost:2181";
    private int sessionTimeout = 2000;
    private ZooKeeper zooKeeper;

    public static void main(String[] args) throws Exception {
        // 获取zk连接
        DistributeServer server = new DistributeServer();
        server.getConnect();
        // 注册服务器
        server.regist(args[0]);

        // 启动 (睡眠)
        Thread.sleep(100000);
    }

    private void regist(String host) throws Exception {
        // 临时 + 序列号
        String s = zooKeeper.create("/servers/" + host, host.getBytes(StandardCharsets.UTF_8),
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println(host + " 已经上线...");
    }

    private void getConnect() throws IOException {
        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {

            }
        });
    }
}
  • 客户端,通过Watch端口监听
public class DistributeClient {
    private ZooKeeper zooKeeper;
    private String connectString = "localhost:2181";
    private int sessionTimeout = 2000;

    public static void main(String[] args) throws Exception {
        // 获取zk连接
        DistributeClient client = new DistributeClient();
        client.getConnect();
        // 监听节点变化
        client.getServerList();
        // 业务逻辑...
        Thread.sleep(1000000);
    }

    private void getServerList() throws Exception {
        List<String> children = zooKeeper.getChildren("/servers", true);
        List<String> servers = new ArrayList<>();
        // 遍历每一个节点
        for (String child : children) {
            byte[] data = zooKeeper.getData("/servers/" + child, false, null);
            servers.add(new String(data));
        }
        System.out.println(servers);
    }

    private void getConnect() throws Exception {
        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getType() == Event.EventType.NodeChildrenChanged) {
                    try {
                        getServerList();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(watchedEvent);
                }
            }
        });
    }
}

分布式锁

API实现

  • 收到请求后,在/locks创建一个临时顺序节点,序号最小的节点,有操作权
  • 其他业务监听locks的子节点变化,判断自己是否是最小的节点
  • 执行完业务后,删除节点,下面的节点收到通知,进行业务操作
  • 测试类
public class DistributeLockTest {

    public static void main(String[] args) throws Exception {
        DistributeLock lock1 = new DistributeLock();
        DistributeLock lock2 = new DistributeLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock1.zkLock();
                    System.out.println(Thread.currentThread() + " 启动...");
                    Thread.sleep(10000);
                    lock1.unZkLock();
                    System.out.println(Thread.currentThread() + " 结束...");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock2.zkLock();
                    System.out.println(Thread.currentThread() + " 启动...");
                    Thread.sleep(10000);
                    lock2.unZkLock();
                    System.out.println(Thread.currentThread() + " 结束...");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
  • zk锁
public class DistributeLock {
    // zookeeper服务端
    private final String connectString = "localhost:2181";
    // 超时时间
    private final int sessionTimeout = 2000;
    // zk客户端
    private final ZooKeeper zooKeeper;
    // 前一个节点的路径
    private String lastPath;
    // 保证连接成功
    private CountDownLatch countDownLatch = new CountDownLatch(1);
    // 阻塞等待抢锁
    private CountDownLatch waitLatch = new CountDownLatch(1);
    // 当前节点路径
    private String currentNode;

    public DistributeLock() throws Exception {
        // 获取连接
        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                if (watchedEvent.getType() == Event.EventType.NodeDeleted &&
                    watchedEvent.getPath().equals(lastPath)) {
                    System.out.println("watchedEvent: " + watchedEvent);
                    // 删除前一个节点的路径
                    waitLatch.countDown();
                }
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    // 连接事件
                    countDownLatch.countDown();
                }
            }
        });
        // 等待zk连接上
        countDownLatch.await();
        // 判断跟节点是否存在
        Stat stat = zooKeeper.exists("/locks", false);
        if (stat != null) {
            return;
        }
        // 创建根节点
        zooKeeper.create("/locks", "locks".getBytes(StandardCharsets.UTF_8),
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    // 加锁
    public void zkLock() throws Exception{
        // 创建临时节点 判断创建的节点是否是最小的序号节点
        currentNode = zooKeeper.create("/locks/" + "lock-", null,
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
        List<String> children = zooKeeper.getChildren("/locks", false);
        // 只有一个,直接获取锁
        if (children.size() == 1) {
            return;
        } else {
            // 如果是,获取到锁;否则,监听前一个节点
            // 排序
            Collections.sort(children);
            String sub = currentNode.substring("/locks/".length());
            // 获取指定节点的位置
            int idx = children.indexOf(sub);
            if (idx == -1) {
                System.out.println("数据异常");
            } else if (idx == 0) {
                return;
            } else {
                // 监听前一个节点
                lastPath = "/locks/" + children.get(idx - 1);
                zooKeeper.getData(lastPath, true, null);
                // 阻塞等待
                waitLatch.await();
                return;
            }
        }


    }
    // 解锁
    public void unZkLock() throws Exception{ 
        // 删除节点
        zooKeeper.delete(currentNode, -1);
    }
}

Curator框架

  • 通过工厂类获取zookeeper客户端
public class CuratorLockTest {
    public static void main(String[] args) {
        // 创建锁
        InterProcessMutex lock1 = new InterProcessMutex(getCuratorFramework(), "/locks");
        InterProcessMutex lock2 = new InterProcessMutex(getCuratorFramework(), "/locks");
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock1.acquire();
                    System.out.println(Thread.currentThread() + " 获取到锁...");
                    // 重入
                    lock1.acquire();
                    Thread.sleep(5000);
                    lock1.release();
                    lock1.release();
                    System.out.println(Thread.currentThread() + " 释放锁...");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock2.acquire();
                    System.out.println(Thread.currentThread() + " 获取到锁...");
                    // 重入
                    lock2.acquire();
                    Thread.sleep(5000);
                    lock2.release();
                    lock2.release();
                    System.out.println(Thread.currentThread() + " 释放锁...");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    private static CuratorFramework getCuratorFramework() {
        // 3秒后重试3次
        ExponentialBackoffRetry policy = new ExponentialBackoffRetry(3000, 3);
        // 工厂类创建
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString("localhost:2181")
                .connectionTimeoutMs(2000)
                .sessionTimeoutMs(2000)
                .retryPolicy(policy).build();//重试策略
        client.start();
        System.out.println("zookeeper 启动...");
        return client;
    }
}