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。

 

posted on 2018-09-19 15:44  脆皮软心  阅读(156)  评论(0编辑  收藏  举报