zookeeper分布式原理实战解析

1.本实验基于centos7.3 +虚拟机VM+zookeeper-3.4.5.tar.gz环境下安装了三台集群测试。
安装参考:http://www.linuxidc.com/Linux/2016-09/135052.htm
启动zookeeper集群shell脚本

 #!/bin/bash
cd /opt/apps/zookeeper/bin 
./zkServer.sh start 
ssh -tt root@192.168.32.131 > /dev/null 2>&1 << remotessh
cd /opt/apps/zookeeper/bin
./zkServer.sh start
exit 
remotessh 
ssh -tt root@192.168.32.130 > /dev/null 2>&1 << remotessh
cd /opt/apps/zookeeper/bin 
./zkServer.sh start 
exit 
remotessh

停止脚本:

 #!/bin/bash 
cd /opt/apps/zookeeper/bin 
./zkServer.sh stop 
ssh -tt root@192.168.32.130 > /dev/null 2>&1 << remotessh 
cd /opt/apps/zookeeper/bin 
./zkServer.sh stop 
exit 
remotessh 
ssh -tt root@192.168.32.131 > /dev/null 2>&1 << remotessh 
cd /opt/apps/zookeeper/bin 
./zkServer.sh stop 
exit 
remotessh

2.zookeeper的服务上下线监听:
结构流程图:
这里写图片描述
实验测试:
服务端代码:

package com.ljt.zk.zkdist;

import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooKeeper.States;
/**
 * 
 * <p>Title: DistributedServer</p>
 * <p> 功能描述:: </p>
 * <p>Company: adteach </p> 
 * @author  刘建涛 * 
 * @date    2017年7月7日上午11:09:25
 * @version 1.0
 */
public class DistributedServer {
    // private static final String connectString =
    // "192.168.32.129:2181,192.168.32.130:2182,192.168.32.131:2183";
    private static final String connectString = "192.168.32.129:2181";
    private static final int sessionTimeout = 15000;
    private static final String parentNode = "/servers";

    private ZooKeeper zk = null;

    /**
     * 创建到zk的客户端连接
     * 
     * @throws Exception
     */
    public void getConnect(String agr) throws Exception {


        //连接不成功等待连接
        while (true) {

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

                    try {
                        zk.getChildren("/servers", true);
                        // 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
                        System.out.println(event.getType() + "---" + event.getPath());
                        // 利用zk连接注册服务器信息
                          registerServer(agr);

                        // 启动业务功能
                         handleBussiness(agr);
                    } catch (Exception e) {
                    }
                }
            });

            if(States.CONNECTED != zk.getState()){
                CountDownLatch connectedLatch = new CountDownLatch(1);
                System.err.println("等待连接……");
                connectedLatch.await();
            }else{
                System.err.println("连接成功!!!");
                break;
            }
        }

    }

    /**
     * 向zk集群注册服务器信息
     * 
     * @param hostname
     * @throws Exception
     */
    public void registerServer(String hostname) throws Exception {

        String create = zk.create(parentNode + "/server", hostname.getBytes(), Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);
        System.out.println(hostname + "is online.." + create);

    }

    /**
     * 业务功能
     * 
     * @throws InterruptedException
     */
    public void handleBussiness(String hostname) throws InterruptedException {
        System.out.println(hostname + "start working.....");
        Thread.sleep(Long.MAX_VALUE);
    }

    public static void main(String[] args) throws Exception {

        // 获取zk连接
        DistributedServer server = new DistributedServer();
        server.getConnect(args[0]);



    }

}

启动时向main函数中注入参数结果如下:
打成jar包运行命令:java -jar jar包 参数
log4j:WARN No appenders could be found for logger (org.apache.zookeeper.ZooKeeper).
log4j:WARN Please initialize the log4j system properly.
等待连接……
None—null
www.ljt.cos01is online../servers/server0000000002
www.ljt.cos01start working…..

客服端代码:

package com.ljt.zk.zkdist;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooKeeper.States;
/***
 * 
 * <p>Title: DistributedClient</p>
 * <p> 功能描述:: </p>
 * <p>Company: adteach </p> 
 * @author  刘建涛 * 
 * @date    2017年7月7日上午11:12:48
 * @version 1.0
 */
public class DistributedClient {

    private static final String connectString = "192.168.32.129:2181,192.168.32.130:2182,192.168.32.131:2183";
    private static final int sessionTimeout = 8000;
    private static final String parentNode = "/servers";
    // 注意:加volatile的意义何在?
    private volatile List<String> serverList;
    private ZooKeeper zk = null;

    /**
     * 创建到zk的客户端连接
     * 
     * @throws Exception
     */
    public void getConnect() throws Exception {

        zk = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                // 收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
                try {
                    // 重新更新服务器列表,并且注册了监听
                    getServerList();

                } catch (Exception e) {
                }
            }
        });
        CountDownLatch connectedLatch = new CountDownLatch(1);
        if (States.CONNECTING == zk.getState()) {
            try {
                connectedLatch.await();
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
        }

    }

    /**
     * 获取服务器信息列表
     * 
     * @throws Exception
     */
    public void getServerList() throws Exception {

        // 获取服务器子节点信息,并且对父节点进行监听
        List<String> children = zk.getChildren(parentNode, true);

        // 先创建一个局部的list来存服务器信息
        List<String> servers = new ArrayList<String>();
        for (String child : children) {
            // child只是子节点的节点名
            byte[] data = zk.getData(parentNode + "/" + child, false, null);
            servers.add(new String(data));
        }
        // 把servers赋值给成员变量serverList,已提供给各业务线程使用
        serverList = servers;

        // 打印服务器列表
        System.out.println(serverList);

    }

    /**
     * 业务功能
     * 
     * @throws InterruptedException
     */
    public void handleBussiness() throws InterruptedException {
        System.out.println("client start working.....");
        Thread.sleep(Long.MAX_VALUE);
    }

    public static void main(String[] args) throws Exception {

        // 获取zk连接
        DistributedClient client = new DistributedClient();
        client.getConnect();
        // 获取servers的子节点信息(并监听),从中获取服务器信息列表
        client.getServerList();

        // 业务线程启动
        client.handleBussiness();

    }

}

3.分布式共享锁
分布式锁结构流程图:
这里写图片描述
这里写图片描述

完整代码:

package com.ljt.zk.zklock;

import java.util.Collections;
import java.util.List;
import java.util.Random;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

public class DistributedClientLock {


    // 会话超时
    private static final int SESSION_TIMEOUT = 2000;
    // zookeeper集群地址
    private String hosts = 
             "192.168.32.129:2181,192.168.32.130:2182,192.168.32.131:2183";
    private String groupNode = "locks";
    private String subNode = "sub";
    private boolean haveLock = false;

    private ZooKeeper zk;
    // 记录自己创建的子节点路径
    private volatile String thisPath;

    /**
     * 连接zookeeper
     */
    public void connectZookeeper() throws Exception {
        zk = new ZooKeeper(hosts, SESSION_TIMEOUT, new Watcher() {
            public void process(WatchedEvent event) {
                try {

                    // 判断事件类型,此处只处理子节点变化事件
                    if (event.getType() == EventType.NodeChildrenChanged && event.getPath().equals("/" + groupNode)) {
                        //获取子节点,并对父节点进行监听
                        List<String> childrenNodes = zk.getChildren("/" + groupNode, true);
                        String thisNode = thisPath.substring(("/" + groupNode + "/").length());
                        // 去比较是否自己是最小id
                        Collections.sort(childrenNodes);
                        if (childrenNodes.indexOf(thisNode) == 0) {
                            //访问共享资源处理业务,并且在处理完成之后删除锁
                            doSomething();

                            //重新注册一把新的锁
                            thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
                                    CreateMode.EPHEMERAL_SEQUENTIAL);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        // 1、程序一进来就先注册一把锁到zk上
        thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
                CreateMode.EPHEMERAL_SEQUENTIAL);

        // wait一小会,便于观察
        Thread.sleep(new Random().nextInt(1000));

        // 从zk的锁父目录下,获取所有子节点,并且注册对父节点的监听
        List<String> childrenNodes = zk.getChildren("/" + groupNode, true);

        //如果争抢资源的程序就只有自己,则可以直接去访问共享资源 
        if (childrenNodes.size() == 1) {
            doSomething();
            thisPath = zk.create("/" + groupNode + "/" + subNode, null, Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL_SEQUENTIAL);
        }
    }

    /**
     * 处理业务逻辑,并且在最后释放锁
     */
    private void doSomething() throws Exception {
        try {
            System.out.println("gain lock: " + thisPath);
            Thread.sleep(2000);
            // do something
        } finally {
            System.out.println("finished: " + thisPath);

            //  删除节点
            zk.delete(this.thisPath, -1);
        }
    }

    public static void main(String[] args) throws Exception {
        DistributedClientLock dl = new DistributedClientLock();
        dl.connectZookeeper();
        Thread.sleep(Long.MAX_VALUE);
    }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ljtyxl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值