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:
-
客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。
-
客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听/lock的子节点变更消息,获得子节点变更通知后重复此步骤直至获得锁;
-
执行业务代码;
-
完成业务流程后,删除对应的子节点释放锁。
步骤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,并在/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; } }
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。