zookeeper原理及应用

BitTigerio 2017-12-07

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是GoogleChubby一个开源的实现。

Apache ZooKeeper is an effort to develop and maintain an open-source server which enables highly reliable distributed coordination.

原理介绍和说明

  • 一致性算法

ZooKeeper以Fast Paxos(帕克索斯)算法为基础,让集群中的每个zk实例数据保持一致。一般部署集群,机器数设置为奇数个,更容易满足>N/2的投票条件。

  • 存储模型
/
├── app1
│   ├── p_1
│   ├── p_2
│   └── p_3
└── app2

类似操作系统文件夹的树型模型,与文件夹的区别在于,每个节点上可存储数据(不超过1M),每个节点上的数据可带版本号。

  • zNode节点类型

2个划分维度:按节点是否可持久化存储,分为持久节点与临时节点;按节点序号是否可顺序递增(类似mysqlauto_increment),分为顺序节点及非顺序节点,

注:3.6.0版本以后,还新增了Container Nodes(容器节点),该节点的特点是,如果其下的所有子节点都被删除,该节点也会在将来某个时间被删除。

ZooKeeper has the notion of container nodes. Container nodes are special purpose nodes useful for recipes such as leader, lock, etc. When the last child of a container is deleted, the container becomes a candidate to be deleted by the server at some point in the future.

持久节点 (client创建持久节点后,就算与zk断开,节点仍然保存在zk)

持久顺序节点 (eg: /order/quartz/wm000001 , /order/quartz/wm000002, /order/quartz/wm000003...)

临时节点 (clientzk断开连接后,节点自动删除)

临时顺序节点

  • 事件监听
    • Created event (节点创建事件)
    • Deleted event (节点删除事件)
    • Changed event (节点的数据变化事件)-zkClient.subscribeChildChanges(); //订阅子节点的变化
    • zkClient.subscribeDataChanges();//订阅某节点的数据变化(包括数据被删除)事件
    • zkClient.subscribeStateChanges();//订单状态变化(状态包括:连接,断开,认证失败等等)

以上为常用事件,其它事件请参考官方文档。实际使用中,很少用原生写法来监听事件,而是借助一些第三方的开源zk客户端,比如zkClient来监听事件。

  • ACL(Access Control List)权限控制

    • 每个节点有5种操作权限:Create、Read、Write、Delete、Admin 简称crwda。其中:Delete是指对子节点是否具有删除权限,其它4种权限指对自身节点的操作权限。
    • 身份认证方式:world:默认方式,无限制,全世界均能访问。auth: 在上下文中添加授权用户。digest: 用户名/密码认证ip: ip地址认证

应用场景

  • 应用场景1:分布式配置
zookeeper原理及应用

要点:配置信息保存在dbzk中(保险起见,数据安全性更高),弄一个后台管理界面,对配置修改后,先保存到db,然后同步写入zk的节点中。应用启动时,先连到zk上读取节点中的配置,同时监听节点的数据变化,当配置变化时,得到实时通知。

  • 应用场景2:消除单点故障(Single Point of Failure,SPOF)
└── ./OrderNoService
        ├── A0000001
                10.0.0.1:8001
        └── A0000002
                10.0.0.2:8001

多个服务实例,启动时在zk上临时顺序节点,服务的调用方约定取最小节点为Master,当Master挂掉后,节点自动删除,调用方得到事件通知,取新的最小节点来调用(相当于slave提升为master)

  • 应用场景3:去中心化zookeeper原理及应用

上图中,左边为传统中心化的架构,缺点是每次有新的服务实例加入或下线,都要调整nginx中心节点的配置(不管是人工,还是借助工具自动),不利于云时代的动态弹性调整,而且整体的可用性强依赖于中心节点,一旦中心节点(中心集群)全挂掉,系统就不可用了。

  • 应用场景4:分布式锁
Order
    └── 3456890
        ├── 000001
        ├── 000002
        └── 000003

原理:以多个程序运行实例同时在处理订单3456890为例,每个程序运行实例处理前,创建一个临时顺序节点,然后检查自己创建的节点是否为最小,如果不是,表明没抢到锁,如果是,表示抢到了锁,抢到锁的程序处理完以后,删除该节点表示释放锁。同时,其它处于等候状态的程序,为了实时得到锁的释放通知,均监听父节点Order/3456890的子节点变化,发现子节点变化时,重复刚才的检测过程,直到自己创建的节点变成最小为止。与redis SETNX之类的分布式锁相比,zk的分布式锁,还能实现解决Top N之类的有限资源竞争问题(类似并发中的信号量)。比如:一堆程序要打印,但是只有2台打印机(或者打印队列的长度只有2,最多同时只能允许2个程序提交打印任务), 类似刚才的思路 ,可以检测最小的前2个节点,只有创建最小前2个节点的程序,才认为是拿到了信号,允许提交打印任务。

  • 应用场景5:分布式队列
Queue
    └── Queue1
        ├── 000001
        ├── 000002
        └── 000003

如上图,创建Queue/Queue1做为一个队列(或Topic),然后每创建一个顺序节点,视为一条消息(节点存储的数据即为消息内容),生产者每次创建一个新节点,做为消息发送,消费者监听Queue1的子节点变化(或定时轮询),每次取最小节点当做消费消息,处理完后,删除该节点。相当于实现了一个FIFO(先进先出)的队列。注:zk框架强制的是CP(一致性),而非专为高并发、高性能场景设计的,如果在高并发,qps很高的情况下,分布式队列需酌情考虑。

  • 应用场景7:生成分布式唯一id
Order
      └── OrderId
        ├── 000001
        ├── 000002
        └── 000003

思路:每次要生成一个新id时,创建一个持久顺序节点,创建操作返回的节点序号,即为新id,然后把比自己节点小的删除即可。

终篇

目前很多开源项目,几乎都是或多或少依赖zookeeper,比如:dubbo,disconf,kafka,...

分布式环境中,zk能用于什么场景,基本上取决于开发人员的想象力!

相关推荐