ZK使用总结

基本概念

《ZK使用总结》

ZAB协议

《ZK使用总结》

客户端脚本

《ZK使用总结》

javaAPI

《ZK使用总结》

ZKClient & Curator

《ZK使用总结》

应用场景

数据发布订阅

《ZK使用总结》

负载均衡

《ZK使用总结》

命名服务

《ZK使用总结》

分布式协调/通知

《ZK使用总结》

集群管理

《ZK使用总结》

Master选举

《ZK使用总结》

分布式锁

《ZK使用总结》

分布式队列

《ZK使用总结》

使用优化

《ZK使用总结》

(一)分布式基础知识

  • 分布式的特点:分布性、对等性、并发性、缺乏全局时钟、故障总会发生
  • 分布式环境下的各种问题:通讯异常、网络分区、成功失败超时三态、节点故障
  • 从ACID到BASE的变化
  • 一致性协议:
    • 2pc(请求处理–>提交确认)与3pc(事务处理能力询问–>处理后待提交–>提交确认)的特点及优缺点比较
    • Paxos协议的原理及在Chubby、Hypertable上的实践。

(二)Zookeeper可以保证的分布式一致性特性:

  • 顺序一致性:同一个客户端发起的事务请求,严格按其顺序处理
  • 原子性:事务请求处理的原子性
  • 单一视图:无论客户端连接到哪个服务器,看到的数据模型是一致的
  • 可靠性:对事务的处理完成并反馈后,状态会一致保留直到被其他事务改变
  • 实时性:一定时间段内,客户端最终一定能从服务器上读取到最新的数据状态

(三)Zookeeper中的核心概念/特性:

全部存储在内存中的树形数据节点ZNode,分为持久(顺序)型与临时(顺序)节点(生命周期与客户端会话绑定),每个ZNode只能由一台服务器创建,且节点的sequential自增数字保障兄弟节点按顺序无重复

  • 三种集群角色

    • Leader:处理事务请求并保证事务请求的顺序性(事务指能够改变Zookeeper服务状态的操作,一般包括数据节点的创建删除与内容更新、客户端会话创建与失效。每一个事务有全局唯一的ZXID);集群内部各服务器的调度
    • Follower:处理客户端非事务请求、转发事务请求给Leader、参与事务请求Proposal投票、参与Leader选举投票
    • Observer:只处理非事务服务,不参与任何形式的投票
  • Stat数据结构维护当前ZNode的三个数据版本:当前版本version、当前子节点版本cversion、ACL版本aversion

  • 客户端与服务端TCP长连接的会话Session管理,分桶管理策略通过设定固定周期的超时检查,批量清理超时会话。

  • 客户端利用API可对数据节点进行如下操作:创建会话、创建节点、删除节点、读取数据、更新数据、检测节点是否存在、权限控制。

  • 常用开源的两款zookeeper客户端:ZkClient、Curator。

  • Watcher事件监听:客户端通过监听特点节点上的特定事件,实现分布式协调服务

  • ACL:类似于UNIX文件系统的权限控制,包括增、删、读、写、admin设置等五种权限

  • Jute为序列化组件

  • Zookeeper将所有节点的路径、数据内容、ACL等信息组成DataTree全部存储在内存中,其底层数据结构是ConcurrentHashMap,其key是数据节点的path,而value则是真正的数据内容DataNode。

  • 通过ZKDatabase管理所有会话、DataTree存储和事务日志,并定时dump快照到磁盘,同时也方便在启动时从磁盘上的事务日志和快照数据文件恢复成一个完整的内存数据库

说明:ZXID是一个64位的数字,其中低32位针对客户端每一个事务请求,Leader服务器在产生一个新的事务proposal时进行+1,高32位代表本轮Leader周期的epoch标号,当新选举一个Leader后会对此epoch+1以区分出Leader周期的变化。集群中拥有XZID最大的proposal的机器会成为Leader,因为它一定具有所有已经提交的提案。

(四)Zookeeper的核心协议ZAB:

  • 主备模型架构保证同一时刻只有一个主进程处理事务请求并广播状态,并能保证全局的变更系列被顺序应用;

  • 所有事务请求必须由一个全局唯一的服务器Leader来协调处理,其他事务请求按照类似两阶段提交的过程向Follower服务器发送proposal提议;

  • ZAB协议包括两种基本模式:崩溃恢复和消息广播

    • 崩溃恢复:当服务启动、Leader网络中断或退出、重启等进去恢复模式,选举产生Leader并在过半的机器从该Leader中国完成了数据同步后退出此模式进行消息广播模式并提供服务,因此只要集群中存在过半的服务器能够彼此进行正常通信,就可以选举出新的Leader并再次进入消息广播模式;
    • 消息广播:Leader为每个事物请求生成ZXID的事务proposal广播给Follower,Follower接收到后以事务日志的形式写入本地磁盘并ACK响应,当Leader接收到过半Follower的ACK后,广播一个commit消息给所有的Follower通知事务提交,同时自身提交,Follower接到commit后也完成提交从而完成这个事务处理。

(五)ZAB协议与Paxos算法的区别于联系:

  • 联系:都有Leader、Follower、epoch值;

  • 区别:

    • Paxos在新选举的主进程中会进行两阶段的工作,第一阶段读阶段主进程通过和其他进程的通信来收集上一个主进程的提案并提交;第二阶段为写阶段即新主进程开始提出自己的提案;
    • ZAB协议在读阶段之后额外引入一个同步阶段,在此阶段中新的Leader会确保存在过半的Follower已经提交了之前Leader周期中所有的事务proposal,从而保证在新Leader提交proposal之前,所有的Follower都完成了对之前所有事务proposal的提交。同步阶段完成后再进入写阶段。

(六)ZooKeeper典型8大应用场景及对应的特性:

  • 数据发布订阅:对统一配置信息等数据可以通过在Zookeeper创建一个数据节点并让客户端进行监听,主要利用了Zookeeper的Watcher监听特性;
  • 负载均衡:创建一个节点,负载应用把自己的服务地址写到此节点下,如果此应用挂掉,则此子节点消失
  • 命名服务:利用Zookeeper创建顺序无重复子节点的特性;
  • 分布式协调/通知:不同客户端都对Zookeeper上的同一个数据节点进行watcher注册,监听数据节点的变化,当发生变化所有订阅的客户端接收到通知并进行处理;
  • 集群管理:利用了watcher监听与临时节点在会话失效自动清除的特性。同时,各服务器可以讲运行状态信息写入到临时节点中进而有助于Leader收集负载信息;
  • Master选举:所有客户端创建同一个path的数据节点,只有一个能成功,即为Master;
  • 分布式锁:创建临时节点,谁成功即获得锁。另外,根据创建时不同的类型-序号,根据一定的规则可以模拟出共享锁、读写锁等;
  • 分布式队列:每个客户端在指定节点下创建临时节点,然后获取该指定节点下的所有子节点并判断自己是否是序号最小的节点,如果是则可以进行处理,如果不是则进入等待并监听比自己序号小的最后一个节点,待接到watcher通知后,重复检查。

相关问题

ZK选举过程

当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法使用ZAB协议:

  1. 选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;
  2. 选举线程首先向所有Server发起一次询问(包括自己);
  3. 选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中;
  4. 收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次要投票的Server;
  5. 线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2 + 1的Server票数, 设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。
    通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数最好是奇数2n+1,且存活的Server的数目不得少于n+1

master/slave之间通信

Storm:定期扫描
PtBalancer:节点监听

节点变多时,PtBalancer速度变慢

原因有五:

  1. ZK有1MB 的传输限制。 实践中ZNode必须相对较小,而队列包含成千上万的消息,非常的大。
  2. 如果有很多节点,ZK启动时相当的慢。 而使用queue会导致好多ZNode. 你需要显著增大 initLimit 和 syncLimit.
  3. Node很大的时候很难清理。Netflix不得不创建了一个专门的程序做这事。
  4. 当很大量的包含成千上万的子节点的ZNode时, ZK的性能变得不好
  5. ZK的数据库完全放在内存中。 大量的Queue意味着会占用很多的内存空间。
    尽管如此, Curator还是创建了各种Queue的实现。 如果Queue的数据量不太多,数据量不太大的情况下,酌情考虑,还是可以使用的。

客户端对ServerList的轮询机制是什么

随机,客户端在初始化( new ZooKeeper(String connectString, int sessionTimeout, Watcher watcher) )的过程中,将所有Server保存在一个List中,然后**随机打散,形成一个环。之后从0号位开始轮训使用。 **
两个注意点:

  • Server地址能够重复配置,这样能够弥补客户端无法设置Server权重的缺陷,但是也会加大风险。(比如: 192.168.1.1:2181,192.168.1.1:2181,192.168.1.2:2181).
  • 如果客户端在进行Server切换过程中耗时过长,那么将会收到SESSION_EXPIRED. 这也是上面第1点中的加大风险之处。

客户端如何正确处理CONNECTIONLOSS(连接断开) 和 SESSIONEXPIRED(Session 过期)两类连接异常

  • 在ZooKeeper中,服务器和客户端之间维持的是一个长连接
  • 在 SESSION_TIMEOUT 时间内,服务器会确定客户端是否正常连接(客户端会定时向服务器发送heart_beat),服务器重置下次SESSION_TIMEOUT时间。
  • 在正常情况下,Session一直有效,并且zk集群所有机器上都保存这个Session信息。
  • 在出现问题情况下,客户端与服务器之间连接断了(客户端所连接的那台zk机器挂了,或是其它原因的网络闪断),这个时候客户端会主动在地址列表(初始化的时候传入构造方法的那个参数connectString)中选择新的地址进行连接。

好了,上面基本就是服务器与客户端之间维持长连接的过程了。在这个过程中,用户可能会看到两类异常CONNECTIONLOSS(连接断开) 和SESSIONEXPIRED(Session 过期)。

CONNECTIONLOSS发生在上面红色文字部分,应用在进行操作A时,发生了CONNECTIONLOSS,此时用户不需要关心我的会话是否可用,应用所要做的就是等待客户端帮我们自动连接上新的zk机器,一旦成功连接上新的zk机器后,确认刚刚的操作A是否执行成功了。

一个客户端修改了某个节点的数据,其它客户端能够马上获取到这个最新数据吗

  • ZooKeeper不能确保任何客户端能够获取(即Read Request)到一样的数据(*最终一致)
  • 除非客户端自己要求:方法是客户端在获取数据之前调用org.apache.zookeeper.AsyncCallback.VoidCallback, java.lang.Object) sync.
  • 通常情况下(这里所说的通常情况满足:1. 对获取的数据是否是最新版本不敏感,2. 一个客户端修改了数据,其它客户端是否需要立即能够获取最新),可以不关心这点。
  • 在其它情况下,最清晰的场景是这样:ZK客户端A对 /my_test 的内容从 v1->v2, 但是ZK客户端B对 /my_test 的内容获取,依然得到的是 v1. 请注意,这个是实际存在的现象,当然延时很短。解决的方法是客户端B先调用 sync(), 再调用 getData().

ZK为什么不提供一个永久性的Watcher注册机制

不支持用持久Watcher的原因很简单,永久性的Watcher很费资源,占用带宽,(相当于推模式,推模式的弊端),而是客户端用的时候注册拉去。
使用watch需要注意的几点

  • Watches通知是一次性的,必须重复注册.
  • 发生CONNECTIONLOSS之后,只要在session_timeout之内再次连接上(即不发生SESSIONEXPIRED),那么这个连接注册的watches依然在。
  • 节点数据的版本变化会触发NodeDataChanged,注意,这里特意说明了是版本变化。存在这样的情况,只要成功执行了setData()方法,无论内容是否和之前一致,都会触发NodeDataChanged。
  • 对某个节点注册了watch,但是节点被删除了,那么注册在这个节点上的watches都会被移除。
  • 同一个zk客户端对某一个节点注册相同的watch,只会收到一次通知。
  • Watcher对象只会保存在客户端,不会传递到服务端。

能否收到每次节点变化的通知

如果节点数据的更新频率很高的话,不能!!!

原因在于:当一次数据修改,通知客户端,客户端再次注册watch,在这个过程中,可能数据已经发生了许多次数据修改,因此,千万不要做这样的测试:”数据被修改了n次,一定会收到n次通知”来测试server是否正常工作。(我曾经就做过这样的傻事,发现Server一直工作不正常?其实不是)。即使你使用了GitHub上这个客户端也一样。

能为临时节点创建子节点吗

不能。

是否可以拒绝单个IP对ZK的访问和操作

ZK本身不提供这样的功能,它仅仅提供了对单个IP的连接数的限制。你可以通过修改iptables来实现对单个ip的限制

在getChildren(String path, boolean watch)是注册了对节点子节点的变化,那么子节点的子节点变化能通知吗

不能

创建的临时节点什么时候会被删除,是连接一断就删除吗?延时是多少?

连接断了之后,ZK不会马上移除临时数据,只有当SESSIONEXPIRED之后,才会把这个会话建立的临时数据移除。
因此,用户需要谨慎设置Session_TimeOut

zookeeper是否支持动态进行机器扩容?如果目前不支持,那么要如何扩容呢?

截止3.4.3版本的zookeeper,还不支持这个功能,在3.5.0版本开始,支持动态加机器了,期待下吧: https://issues.apache.org/jira/browse/ZOOKEEPER-107

ZooKeeper集群中服务器之间是怎样通信的?

Leader服务器会和每一个Follower/Observer服务器都建立TCP连接,同时为每个F/O都创建一个叫做LearnerHandler的实体。LearnerHandler主要负责Leader和F/O之间的网络通讯,包括数据同步,请求转发和Proposal提议的投票等。Leader服务器保存了所有F/O的LearnerHandler。

zookeeper是否会自动进行日志清理?如何进行日志清理?

zk自己不会进行日志清理,需要运维人员进行日志清理

Ref:
《从Paxos到zookeeper分布式一致性原理与实践》
http://www.cnblogs.com/dimmacro/p/4457665.html

    原文作者:jiangmo
    原文地址: https://www.jianshu.com/p/eec133595c68
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞