Zookeeper案例总结目录

  • 1. 服务器动态上下线监听案例
  • 2. ZooKeeper 分布式锁案例
  • 3.Curator 框架实现分布式锁案例


1. 服务器动态上下线监听案例

  • 需求
  • 某分布式系统中,主节点可以有多台,可以动态上下线,任意一台客户端都能实时感知到主节点服务器的上下线。
  • 需求分析
  • 简单描述就是实现集群可以存储服务器的节点信息,客户端实现监听节点变化的功能

zookeeper 假死 zookeeper实例_java-zookeeper

  • 实现过程
  • 首先在集群上登录客户端创建一个节点: create /servers “servers”
  • 在IDEA中实现以下代码:

DistributeClient.java

public class DistributeClient {

    private ZooKeeper zooKeeper;
    private String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private int sessionTimeout = 2000000;

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {

        DistributeClient client = new DistributeClient();

        //1.获取zookeeper连接
        client.getConnect();

        //2.监听/servers下面节点的删除和添加
        client.getServerList();

        //3.执行业务逻辑
        client.business();
    }

    private void business() throws InterruptedException {

        Thread.sleep(Long.MAX_VALUE);
    }

    private void getServerList() throws InterruptedException, KeeperException {

        //获取/servers路径下的所有节点
        List<String> children = zooKeeper.getChildren("/servers", true);

        //保存节点数据
        ArrayList<String> server = new ArrayList<>();

        for (String child : children) {

            //获取节点数据
            byte[] data = zooKeeper.getData("/servers/" + child, false, null);

            //将节点数据添加到集合
            server.add(new String(data));
        }

        System.out.println(server);
    }

    private void getConnect() throws IOException {

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

                try {
                    getServerList();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (KeeperException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

DistributeServer.java

public class DistributeServer {

    private String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private int sessionTimeout = 20000000;
    private ZooKeeper zooKeeper;

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {

        DistributeServer server = new DistributeServer();
        
        //1.获取zk集群连接
        server.getConnect();
        
        //2.注册服务器到集群
        server.register(args[0]);
        
        //3.执行业务逻辑
        server.business();
    }

    private void business() throws InterruptedException {

        Thread.sleep(Long.MAX_VALUE);
    }

    private void register(String hostname) throws InterruptedException, KeeperException {

        //2.注册服务器到集群
        String create = zooKeeper.create("/servers/"+hostname, hostname.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);

        System.out.println(hostname + "is online");

    }

    private void getConnect() throws IOException {

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

            }
        });
    }
}

2. ZooKeeper 分布式锁案例

  • 分布式锁的概念:
  • 比如说"进程 1"在使用该资源的时候,会先去获得锁,"进程 1"获得锁以后会对该资源保持独占,这样其他进程就无法访问该资源,"进程 1"用完该资源以后就将锁释放掉,让其他进程来获得锁,那么通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。
  • 案例分析
  • 过程描述:多个客户端都让服务器创建临时节点来处理业务,为保证在分布式环境下,单个进程依次执行业务操作,所以需要上锁,具体实现步骤如下:
  • ①接收到请求后,在/locks节点下创建一个临时顺序节点
  • ②判断自己是不是当前节点下最小的节点:是,获取到锁;不是,对前一个节点进行监听
  • ③获取到锁,处理完业务后,delete节点释放锁,然后下面的节点将收到通知,重复第二步判断

zookeeper 假死 zookeeper实例_zookeeper_02

DistributeLock.java

public class DistributeLock {

    private final String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
    private final int sessionTimeout = 2000000;
    private final ZooKeeper zooKeeper;

    //等待上一个线程结束之后才会执行后面的代码
    private CountDownLatch connectLatch = new CountDownLatch(1);
    private CountDownLatch waitLatch = new CountDownLatch(1);

    private String waitPath;
    private String currentMode;

    public DistributeLock() throws IOException, InterruptedException, KeeperException {

        //获取连接
        zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {

                //如果连接上connectLatch需要释放
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected){
                    connectLatch.countDown();
                }

                //如果是删除节点并且还是前一个节点路径就释放
                if (watchedEvent.getType() == Event.EventType.NodeDeleted && watchedEvent.getPath().equals(waitPath)){
                    waitLatch.countDown();
                }
            }
        });

        connectLatch.await();

        //判断根节点“/locks”是否存在
        Stat stat = zooKeeper.exists("/locks", false);
        
        //如果根节点不存在就创建根节点
        if (stat == null){

            String rootNode = zooKeeper.create("/locks", "locks".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        }

    }

    //上锁
    public void lock() {

        //创建临时的带序号的节点
        try {
            currentMode = zooKeeper.create("/locks/" + "seq-", 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);

                //获取当前节点序号seq-000000000001
                String thisNode = currentMode.substring("/locks/".length());

                int index = children.indexOf(thisNode);

                //判断当前节点状态
                if (index == -1 ){
                    System.out.println("数据异常");
                }else if (index == 0){
                    return;
                }else {
                    //需要监听前一个节点
                    waitPath = "/locks/"+children.get(index - 1);
                    zooKeeper.getData(waitPath,true,new Stat());

                    waitLatch.await();

                    return;
                }
            }

        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    //解锁
    public void unlock(){
        try {
            zooKeeper.delete(currentMode,1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
}

DistributeLockTest.java

public class DistributeLockTest {

    public static void main(String[] args) throws IOException, InterruptedException, KeeperException {

        final DistributeLock lock1 = new DistributeLock();
        final DistributeLock lock2 = new DistributeLock();

        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    lock1.lock();
                    System.out.println("线程1启动,获取到锁");
                    Thread.sleep(6*1000);

                    System.out.println("线程1释放锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    lock2.lock();
                    System.out.println("线程2启动,获取到锁");
                    Thread.sleep(6*1000);

                    System.out.println("线程2释放锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }
}

3.Curator 框架实现分布式锁案例

  • 原生的 Java API 开发存在的问题
  • (1)会话连接是异步的,需要自己去处理。比如使用 CountDownLatch
  • (2)Watch 需要重复注册,不然就不能生效
  • (3)开发的复杂性比较高的
  • (4)不支持多节点删除和创建。需要自己去递归
  • Curator 是一个专门解决分布式锁的框架,解决了原生 JavaAPI 开发分布式遇到的问题。

Curator实现分布式锁

public class CuratorLockTest {

    public static void main(String[] args) {

        //创建分布式锁1
        InterProcessMutex lock1 = new InterProcessMutex(getCuratorFrameWork(), "/locks");


        //创建分布式锁2
        InterProcessMutex lock2 = new InterProcessMutex(getCuratorFrameWork(), "/locks");

        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    lock1.acquire();
                    System.out.println("线程1获取到锁");

                    lock1.acquire();
                    System.out.println("线程1再次获取到锁");

                    lock1.release();
                    System.out.println("线程1释放锁");
                    lock1.release();
                    System.out.println("线程1再次释放锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    lock2.acquire();
                    System.out.println("线程2获取到锁");

                    lock2.acquire();
                    System.out.println("线程2再次获取到锁");

                    lock2.release();
                    System.out.println("线程2释放锁");
                    lock2.release();
                    System.out.println("线程2再次释放锁");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }

    private static CuratorFramework getCuratorFrameWork() {

        //指数退避重试
        ExponentialBackoffRetry policy = new ExponentialBackoffRetry(30000, 3);

        //通过Curator框架工厂创建客户端对象
        CuratorFramework client = CuratorFrameworkFactory.builder().connectString("hadoop102:2181,hadoop103:2181,hadoop104:2181")
                .connectionTimeoutMs(200000)
                .sessionTimeoutMs(2000000)
                .retryPolicy(policy).build();

        //开启客户端
        client.start();

        System.out.println("客户端启动成功");
        return client;
    }
}