zoukankan      html  css  js  c++  java
  • Zookeeper

    1.Zookeeper官方网站下载  http://zookeeper.apache.org/

    或者用wget命令下载  wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz

    2.安装,直接解压即可  tar -zxvf zookeeper-3.4.10.tar.gz -C /usr/local/

    3.配置文件,在zookeeper的conf目录下,将zoo_sample.cfg拷贝一份,命名为zoo.cfg,cp zoo_sample.cfg  zoo.cfg,zookeeper启动时会读取该配置文件作为默认配置文件

      配置文件解读,修改

    先创建数据目录 data

    在配置文件中指定数据存储路径,保存,退出

     

    4.启动(切换到安装目录的bin目录下): ./zkServer.sh start

    5.关闭(切换到安装目录的bin目录下): ./zkServer.sh stop

    bin目录下,客户端连接命令:  ./zkCli.sh -server localhost:2181

    Zookeeper集群搭建

    • 将下载的zookeeper-3.4.10 命名为zookeeper-3.4.10_1,然后再拷贝2份,做集群用。

    cp -rf zookeeper-3.4.10_1  zookeeper-3.4.10_2
    cp -rf zookeeper-3.4.10_1  zookeeper-3.4.10_3

    • 分别进入zookeeper-3.4.10_1,zookeeper-3.4.10_3,zookeeper-3.4.10_3目录,创建存储目录data,并且在data文件夹下面创建一个叫myid的文件,在文件里写入zoo.cfg配置文件中server.X对应的X值,第一台的内容为 1,第二台的内容为 2,第二台的内容为 3
    • 然后再分别进入conf目录,将zoo_sample.cfg拷贝一份,命名为zoo.cfg,命令: cp zoo_sample.cfg  zoo.cfg,三台做如下配置:

    •  将3台分别启动  ./zkServer.sh  start

    • 查看集群状态,如下图,可以看出,1为leader,2,3为follower。

     集群节点一致性特点

     

    客户端连接服务器

    ./zkCli.sh  -server localhost:2181

     创建节点

    create /n 100       表示在根节点/下创建持久节点n,值为100

    create -s /n 111    表示在根节点/下创建持久顺序节点,节点名字以n开通后面跟10个数字编号,如nxxx,值为111

    create -s /n/ 222  表示在n节点下创建持久顺序节点,节点名字是10个数字编号,如xxx,值为222

    zookeeper节点共分4类,持久节点,持久顺序节点,临时节点,临时顺序节点。

    ephemeralOwner值非0,表示临时节点,如果是永久节点则其值为 0x0

    create -e 表示创建临时节点,-s表示创建顺序节点,-e和-s可以一块使用

    rmr 删除节点

    修改相关命令:

    set path data [version]

    如果我们多次修改,会发现  dataVersion ,也就是数据版本,在不停得发生变化(自增)

    如果我们在set的时候手动去指定了版本号,就必须和上一次查询出来的结果一致,否则 就会报错。

    这个可以用于我们在修改节点数据的时候,保证我们修改前数据没被别人修改过。因为如果别人修改过了,我们这次修改是不会成功的。

    如上第二次把节点0000000001的值改为333后,dataVersion值为2,如果我们下次修改该节点值,指定了dataVersion值但值又不与当前版本一致时,则报错

    若指定的dataVersion值正确,则可以修改

     dubbo在zookeeper中的providers节点

    [zk: localhost:2183(CONNECTED) 12] ls /
    [LOCK, dubbo, n0000000011, zookeeper, n]
    [zk: localhost:2183(CONNECTED) 13] ls /dubbo
    [com.zxp.dubbo.HelloService]
    [zk: localhost:2183(CONNECTED) 14] ls /dubbo/com.zxp.dubbo.HelloService
    [consumers, configurators, routers, providers]
    [zk: localhost:2183(CONNECTED) 15] ls /dubbo/com.zxp.dubbo.HelloService/providers         
    [dubbo%3A%2F%2F192.168.220.1%3A20880%2Fcom.zxp.dubbo.HelloService%3Fanyhost%3Dtrue%26application%3Ddubboprovider%26dubbo%3D2.6.0%26generic%3Dfalse%26interface%3Dcom.zxp.dubbo.HelloService%26methods%3DgetMsg%2CgetHashMap%26pid%3D10916%26revision%3D0.0.1-SNAPSHOT%26side%3Dprovider%26timestamp%3D1550503289819]
    dubbo%3A%2F%2F192.168.220.1%3A20880%2Fcom.zxp.dubbo.HelloService%3Fanyhost%3Dtrue%26application%3Ddubboprovider%26dubbo%3D2.6.0%26gene
    ric%3Dfalse%26interface%3Dcom.zxp.dubbo.HelloService%26methods%3DgetMsg%2CgetHashMap%26pid%3D10916%26revision%3D0.0.1-SNAPSHOT%26side%
    3Dprovider%26timestamp%3D1550503289819 经过UrlDecode解码后,结果如下:
    dubbo://192.168.220.1:20880/com.zxp.dubbo.HelloService?anyhost=true&application=dubboprovider&dubbo=2.6.0&generic=false&interface=com.zxp.dubbo.HelloService&methods=getMsg,getHashMap&pid=10916&revision=0.0.1-SNAPSHOT&side=provider&timestamp=1550503289819

    继续查看该节点的值信息,可以看到是个临时节点
    [zk: localhost:2183(CONNECTED) 18] get /dubbo/com.zxp.dubbo.HelloService/providers/dubbo%3A%2F%2F192.168.220.1%3A20880%2Fcom.zxp.dubbo.HelloService%3Fanyhost%3Dtrue%26application%3Ddubboprovider%26dubbo%3D2.6.0%26generic%3Dfalse%26interface%3Dcom.zxp.dubbo.HelloService%26methods%3DgetMsg%2CgetHashMap%26pid%3D10916%26revision%3D0.0.1-SNAPSHOT%26side%3Dprovider%26timestamp%3D1550503289819
    null
    cZxid = 0x700000074
    ctime = Sun Feb 17 14:55:45 CST 2019
    mZxid = 0x700000074
    mtime = Sun Feb 17 14:55:45 CST 2019
    pZxid = 0x700000074
    cversion = 0
    dataVersion = 0
    aclVersion = 0
    ephemeralOwner = 0x268f9c912ca0005
    dataLength = 0
    numChildren = 0

    如果dubbo服务器断开,则节点自动删除

    用之前的key查值,结果如下

    [zk: localhost:2183(CONNECTED) 25] get /dubbo/com.zxp.dubbo.HelloService/providers/dubbo%3A%2F%2F192.168.220.1%3A20880%2Fcom.zxp.dubbo.HelloService%3Fanyhost%3Dtrue%26application%3Ddubboprovider%26dubbo%3D2.6.0%26generic%3Dfalse%26interface%3Dcom.zxp.dubbo.HelloService%26methods%3DgetMsg%2CgetHashMap%26pid%3D10916%26revision%3D0.0.1-SNAPSHOT%26side%3Dprovider%26timestamp%3D1550503289819
    Node does not exist: /dubbo/com.zxp.dubbo.HelloService/providers/dubbo%3A%2F%2F192.168.220.1%3A20880%2Fcom.zxp.dubbo.HelloService%3Fanyhost%3Dtrue%26application%3Ddubboprovider%26dubbo%3D2.6.0%26generic%3Dfalse%26interface%3Dcom.zxp.dubbo.HelloService%26methods%3DgetMsg%2CgetHashMap%26pid%3D10916%26revision%3D0.0.1-SNAPSHOT%26side%3Dprovider%26timestamp%3D1550503289819

     dubbo在zookeeper中的consumers节点,也是一个临时节点

    [zk: localhost:2183(CONNECTED) 37] ls /dubbo/com.zxp.dubbo.HelloService/consumers
    [consumer%3A%2F%2F192.168.220.1%2Fcom.zxp.dubbo.HelloService%3Fapplication%3Ddubboconsumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.6.0%26interface%3Dcom.zxp.dubbo.HelloService%26methods%3DgetMsg%2CgetHashMap%26pid%3D7924%26revision%3D0.0.1-SNAPSHOT%26side%3Dconsumer%26timestamp%3D1550504306650]
    [zk: localhost:2183(CONNECTED) 38] ls /dubbo/com.zxp.dubbo.HelloService/providers

    经过UrlDecode解码后,结果如下:

    consumer://192.168.220.1/com.zxp.dubbo.HelloService?application=dubboconsumer&category=consumers&check=false&dubbo=2.6.0&interface=com.zxp.dubbo.HelloService&methods=getMsg,getHashMap&pid=7924&revision=0.0.1-SNAPSHOT&side=consumer&timestamp=1550504306650

    当消费方断开后,节点也会自动删除

    zookeeper实现分布式锁

    <dependency>
                <groupId>com.101tec</groupId>
                <artifactId>zkclient</artifactId>
                <version>0.10</version>
    </dependency>
    public class OrderCodeGenerator {
        // 自增长序列
        private static int i = 0;
        // 按照“年-月-日-小时-分钟-秒-自增长序列”的规则生成订单编号
        public String getOrderCode() {
            Date now = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            return sdf.format(now) + ++i;
        }
    }
    public class ZookeeperLock implements Lock {
        private static Logger logger = LoggerFactory.getLogger(ZookeeperLock.class);
    
        private static final String ZOOKEEPER_IP_PORT = "192.168.227.129:2181,192.168.227.129:2182,192.168.227.129:2183";
        private static final String LOCK_PATH = "/LOCK";
    
        private ZkClient client = new ZkClient(ZOOKEEPER_IP_PORT, 3000, 3000, new SerializableSerializer());
    
        private CountDownLatch cdl;
    
        private String beforePath;// 当前请求的节点前一个节点
        private String currentPath;// 当前请求的节点
    
        // 判断有没有LOCK目录,没有则创建
        public ZookeeperLock() {
            if (!this.client.exists(LOCK_PATH)) {
                this.client.createPersistent(LOCK_PATH);
            }
        }
    
        public void lock() {
            //尝试去获取分布式锁失败
            if (!tryLock()) {
                //对次小节点进行监听
                waitForLock();
                lock();
            } 
            else {
                System.out.println(Thread.currentThread().getName() + "-->获得分布式锁!");
            }
        }
        
        public boolean tryLock() {
            // 如果currentPath为空则为第一次尝试加锁,第一次加锁赋值currentPath
            if (currentPath == null || currentPath.length() <= 0) {
                // 在"/LOCK"节点下,创建一个临时顺序节点,值为"lock",
    //currentPath是创建的临时节点路径 如:/LOCK/0000000180,字符串0000000180为自增长的临时节点名称 currentPath = this.client.createEphemeralSequential(LOCK_PATH + '/', "lock"); System.out.println(Thread.currentThread().getName()+"注册的路径-->" + currentPath); } // 获取"/LOCK"节点下面的所有临时节点名称,临时节点名称为自增长的字符串如:0000000100 List<String> childrens = this.client.getChildren(LOCK_PATH); //由小到大排序所有临时子节点名称集合 Collections.sort(childrens); //判断创建的子节点/LOCK/Node-n是否最小,即currentPath,如果当前节点等于childrens中的最小的一个就占用锁 if (currentPath.equals(LOCK_PATH + '/' + childrens.get(0))) { return true; } //找出比创建的临时顺序节子节点/LOCK/Node-n次小的节点,并赋值给beforePath else { int wz = Collections.binarySearch(childrens, currentPath.substring(6)); beforePath = LOCK_PATH + '/' + childrens.get(wz - 1); } return false; } //等待锁,对次小节点进行监听 private void waitForLock() { IZkDataListener listener = new IZkDataListener() { public void handleDataDeleted(String dataPath) throws Exception { logger.info(Thread.currentThread().getName() + ":捕获到DataDelete事件!--"); if (cdl != null) { cdl.countDown(); } } public void handleDataChange(String dataPath, Object data) throws Exception { } }; // 对次小节点进行监听,即beforePath-给排在前面的的节点增加数据删除的watcher this.client.subscribeDataChanges(beforePath, listener); if (this.client.exists(beforePath)) { cdl = new CountDownLatch(1); try { cdl.await(); } catch (InterruptedException e) { e.printStackTrace(); } } this.client.unsubscribeDataChanges(beforePath, listener); } //完成业务逻辑以后释放锁 public void unlock() { // 删除当前临时节点 client.delete(currentPath); } // ========================================== public void lockInterruptibly() throws InterruptedException { } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return false; } public Condition newCondition() { return null; } }
    public class Test {
        private static OrderCodeGenerator ong = new OrderCodeGenerator();
        // 同时并发的线程数
        private static final int NUM = 100;
        // 按照线程数初始化倒计数器,倒计数器
        private static CountDownLatch cdl = new CountDownLatch(NUM);
    
        public static void main(String[] args) {
            for (int i = 0; i < NUM; i++) {
                new Thread(() -> {
                    Lock lock = new ZookeeperLock();
                    try {
                        cdl.await();
                        lock.lock();
                        String orderCode = ong.getOrderCode();
                        // ……业务代码
                        System.out.println(Thread.currentThread().getName() + "得到的id:==>" + orderCode);
                    } catch (Exception e) {
                    } finally {
                        lock.unlock();
                    }
    
                }).start();
                cdl.countDown();
            }
        }
    }
  • 相关阅读:
    重写与重载的区别
    UDP模式与TCP模式的区别
    什么是GC?为什么会有GC?
    centos 7-8 安装 ms sql server 2019
    Phaser3 游戏开发入门——自定义构建Phaser库
    Visual Studio 下C#编译器在解析属性名时如果增加一个get_[您的另一个已经包含在类中属性名]的属性会报错,微软大哥这是什么鬼?
    Visual Studio 2015 Update 3 ISO
    react项目中引用amap
    js 截取网址中的某一段字符串
    解决react下找不到原生高德地图AMap类的问题
  • 原文地址:https://www.cnblogs.com/tyhj-zxp/p/10087442.html
Copyright © 2011-2022 走看看