zoukankan      html  css  js  c++  java
  • Zookeeper学习

    1、 Zookepper介绍*****

    https://www.jianshu.com/p/84ad63127cd1

    2、ZAB Zookepper 原子广播协议

    https://blog.csdn.net/junchenbb0430/article/details/77583955

    3、Zookepper学习内容

    3.1 Zookepper底层如何实现?(ZAB原子消息广播协议、paxos一致性协议)

    3.2 Zookepper分布式锁

    https://blog.csdn.net/qiangcuo6087/article/details/79067136

    下面描述使用zookeeper实现分布式锁的算法流程,假设锁空间的根节点为/lock:

    1. 客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。

    2. 客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听/lock的子节点变更消息,获得子节点变更通知后重复此步骤直至获得锁;

    3. 执行业务代码;

    4. 完成业务流程后,删除对应的子节点释放锁。

    步骤1中创建的临时节点能够保证在故障的情况下锁也能被释放,考虑这么个场景:假如客户端a当前创建的子节点为序号最小的节点,获得锁之后客户端所在机器宕机了,客户端没有主动删除子节点;如果创建的是永久的节点,那么这个锁永远不会释放,导致死锁;由于创建的是临时节点,客户端宕机后,过了一定时间zookeeper没有收到客户端的心跳包判断会话失效,将临时节点删除从而释放锁。

    另外细心的朋友可能会想到,在步骤2中获取子节点列表与设置监听这两步操作的原子性问题,考虑这么个场景:客户端a对应子节点为/lock/lock-0000000000,客户端b对应子节点为/lock/lock-0000000001,客户端b获取子节点列表时发现自己不是序号最小的,但是在设置监听器前客户端a完成业务流程删除了子节点/lock/lock-0000000000,客户端b设置的监听器岂不是丢失了这个事件从而导致永远等待了?这个问题不存在的。因为zookeeper提供的API中设置监听器的操作与读操作是原子执行的,也就是说在读子节点列表时同时设置监听器,保证不会丢失事件。

    最后,对于这个算法有个极大的优化点:假如当前有1000个节点在等待锁,如果获得锁的客户端释放锁时,这1000个客户端都会被唤醒,这种情况称为“羊群效应”;在这种羊群效应中,zookeeper需要通知1000个客户端,这会阻塞其他的操作,最好的情况应该只唤醒新的最小节点对应的客户端。应该怎么做呢?在设置事件监听时,每个客户端应该对刚好在它之前的子节点设置事件监听,例如子节点列表为/lock/lock-0000000000、/lock/lock-0000000001、/lock/lock-0000000002,序号为1的客户端监听序号为0的子节点删除消息,序号为2的监听序号为1的子节点删除消息。

    zookeeper学习中

    所以调整后的分布式锁算法流程如下:

    • 客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推;

    • 客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁;

    • 执行业务代码;

    • 完成业务流程后,删除对应的子节点释放锁。

    3.4 分布式队列DistributedQueue

     队列方面,简单地讲有两种,一种是常规的先进先出队列,另一种是要等到队列成员聚齐之后的才统一按序执行。对于第一种先进先出队列,和分布式锁服务中的控制时序场景基本原理一致,这里不再赘述。

    第二种队列其实是在FIFO队列的基础上作了一个增强。通常可以在 /queue 这个znode下预先建立一个/queue/num 节点,并且赋值为n(或者直接给/queue赋值n),表示队列大小,之后每次有队列成员加入后,就判断下是否已经到达队列大小,决定是否可以开始执行了。这种用法的典型场景是,分布式环境中,一个大任务Task A,需要在很多子任务完成(或条件就绪)情况下才能进行。这个时候,凡是其中一个子任务完成(就绪),那么就去 /taskList 下建立自己的临时时序节点(CreateMode.EPHEMERAL_SEQUENTIAL),当 /taskList 发现自己下面的子节点满足指定个数,就可以进行下一步按序进行处理了。

    3.5 命名服务:统一命名服务(Name Service)

    Name Service 已经是 Zookeeper 内置的功能,你只要调用 Zookeeper 的 API 就能实现。如调用 create 接口就可以很容易创建一个目录节点。

    在zk系统中创建的节点可以保证在分布式服务器上是全局唯一的。

    在zk系统中创建的节点可以保证在分布式服务器上是全局唯一的。

    命名服务提供注册、注销和查看命名等接口。

    import org.apache.zookeeper.CreateMode;  
    import org.apache.zookeeper.KeeperException;  
    import org.apache.zookeeper.ZooKeeper;  
    import org.apache.zookeeper.ZooDefs.Ids;  
      
    public class Naming {  
        private ZooKeeper zk = null; // ZooKeeper对象  
        private String nameroot = "/NameService";  
        private String namerootvalue = "IsNameService";  
        private String namevalue = "IsName";  
      
        /** 
         * @函数:命名服务构造函数 
         * @参数:zk的地址端口 描述:初始化zk实例,创建命名服务根路径 
         */  
        public Naming(String url) {  
            try {  
                // 初始化,如果当前有alive的zk连接则先关闭  
                if (zk != null && zk.getState().isAlive() == true)  
                    zk.close();  
                zk = new ZooKeeper(url, 30000, null); // 重新建立连接  
                System.out.println("zookeeper connect success:url=" + url);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            // 判断是否有/NameService,如果没有,则创建该路径,用来作为所有的集中配置信息的根目录  
            try {  
                if (zk.exists(nameroot, false) == null) {  
                    zk.create(nameroot, namerootvalue.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
                    System.out.println(nameroot + " create success!");  
                }  
            } catch (KeeperException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
                System.out.println(e.getMessage());  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
                System.out.println(e.getMessage());  
            }  
        }  
      
        /** 
         * @函数: 注销zk实例 
         */  
        public void UnNaming() {  
            if (zk != null) {  
                try {  
                    zk.close();  
                    System.out.println("zookeeper close success!");  
                } catch (InterruptedException e) {  
                    // TODO Auto-generated catch block  
                    e.printStackTrace();  
                    System.out.println(e.getMessage());  
                }  
                zk = null;  
            }  
        }  
      
        /** 
         * @函数:注册一个全局名字 
         * @描述:待注册的名字字符串name,在zk中创建一个/NameService/name的znode路径 
         * @参数: 待注册的名字字符串name 
         * @返回值: 0 表示注册成功 -1 表示出错 1 表示该命名已被注册 
         */  
        @SuppressWarnings("finally")  
        public int Registered(String name) {  
            String path = nameroot + "/" + name;  
            int ret = 0;  
            try {  
                if (zk.exists(path, false) == null) {  
                    zk.create(path, namevalue.getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
                    System.out.println(name + " registered success!");  
                } else {  
                    ret = 1;  
                    System.out.println(name + " is exists, can not regist again!");  
                }  
            } catch (KeeperException e) {  
                // TODO Auto-generated catch block  
                ret = -1;  
                e.printStackTrace();  
                System.out.println(e.getMessage());  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                ret = -1;  
                e.printStackTrace();  
                System.out.println(e.getMessage());  
            } finally {  
                return ret;  
            }  
        }  
      
        /** 
         * @函数:注销一个全局名字 
         * @描述:待注销的名字字符串name,在zk中删除/NameService/name的znode路径 
         * @参数: 待注销的名字字符串name 
         * @返回值: 0 表示注销成功 -1 表示出错 1 表示该命名未注册,不存在命名服务系统中 
         */  
        @SuppressWarnings("finally")  
        public int Canceled(String name) {  
            String path = nameroot + "/" + name;  
            int ret = 0;  
            try {  
                if (zk.exists(path, false) != null) {  
                    zk.delete(path, -1);  
                    System.out.println(name + " canceled success!");  
                } else {  
                    ret = 1;  
                    System.out.println(name + " is not exists, can not canceled!");  
                }  
            } catch (KeeperException e) {  
                // TODO Auto-generated catch block  
                ret = -1;  
                e.printStackTrace();  
                System.out.println(e.getMessage());  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                ret = -1;  
                e.printStackTrace();  
                System.out.println(e.getMessage());  
            } finally {  
                return ret;  
            }  
        }  
      
        /** 
         * @函数:获取命名服务系统的所有命名 
         * @描述: 
         * @参数: 
         * @返回值:命名列表 
         */  
        public List<String> Readall() {  
            List<String> namelist = new ArrayList<String>();  
            try {  
                namelist = zk.getChildren(nameroot, false);  
            } catch (KeeperException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            return namelist;  
        }  
      
    }  
    View Code

    3.6 master选举

    3.7  数据的发布 订阅

    3.8  负载均衡

    4、Zookepper 的zkClient

    4.1 zookeeper的zkclient的使用简介

    https://blog.csdn.net/t1dmzks/article/details/78440717

    4.2

    5、总结

      • zookeeper是什么框架? 
        zookeeper是分布式的RPC框架。 
        但是zookeeper远不止于此。zookeeper可以做的事儿太多了,例如配置一致,例如高可用,sub/pub等等。 
        所谓分布式就是跨越多个物理主机,由部署在多个主机上的独立软件对外提供服务的架构,既然有多个组件,就涉及到多个服务的问题,如何保证服务的强一致性,有序性和持久性,这就是zookeeper要解决的问题,通俗来说,我们的软件相当于zookeeper服务端的客户端。

      • zookeeper解决了什么问题? 
        zookeeper本身是用于分布式系统的任务协作,分布式系统一般有几个重大的问题需要解决,1,主节点崩溃恢复问题, 2,从节点崩溃的恢复 3,通讯故障处理 zookeeper提供了优雅的解决方案。 先简单介绍下解决方案,主节点崩溃的时候,zookeeper会重新选举主节点(数字最小的一个), 从节点崩溃时候,主节点重新分发任务。 通讯中断或者故障的时候,zookeeper会试着恢复从节点。 
        zookeeper主要解决了分布式系统的数据一致性问题。

      • zookeeper试用的场景 
        zookeeper并不是万能的,zookeeper的主要功能还是协同任务,所以数据存储上并不能存储大量的数据,zookeeper适用于数据量小并发高的系统。

      • zookeeper用了什么协议? 
        zookeeper用的是zab协议,翻译为zookeeper原子广播消息,后续会有介绍。 
        参见:http://blog.csdn.net/wangpengzhi19891223/article/details/75578153 
        zab协议本身就是分布式一致性算法paxos算法的一种实现,这种算法是保证分布式系统正确执行的算法,涉及提议和选举两个步骤,来保证分布式个子系统之间对下一步执行什么操作有一个共识,参考:http://www.toutiao.com/a6315662880364740865/ 
        http://www.toutiao.com/a6420579712703234305/

        还有一个谷歌的框架和zookeeper很像,叫chubby, chubby并不是开源的,雅虎参考chubby设计思想开发了zookeeper,zookeeper的核心其实就是两点,第一个是选举算法,第二个是一致性算法,即zab。

  • 相关阅读:
    HTML基础之HTML标签-html header(meta,title) html body(p,br,h,form,div,span,input,lable)
    day5_非空即真非零即真
    day5_不能循环删除list-深拷贝、浅拷贝(import copy)
    day5_函数返回值
    day4_局部变量和全局变量
    day5_函数的参数
    python字符串与列表的相互转换
    python自动化返回:no tests were fount Empty test suite
    读取Excel数据封装
    测试工程题
  • 原文地址:https://www.cnblogs.com/wangleBlogs/p/9674941.html
Copyright © 2011-2022 走看看