基于Curator的zookeeper介绍和使用

最好的学习方式当然是实践与理论相结合。在接收开发任务前正好在研究zookeeper。嗯,喜好模型的我,自然是研究zookeeper的理论部分,在此推荐书籍《从PAXOS到ZOOKEEPER分布式一致性原理与实践》。印象及其深刻的一幕在此发生,因研究完上述书籍,对zookeeper的原理十分清楚,便用了一个下午的时间完成了zookeeper模块的实现,当时主管一度怀疑,特地翻了一遍我的代码~~ 呵呵呵,好吧,可能在开发方面我看起来不像是个靠谱的人。

然而 ,这种情况也是极少见的,即研究完一个技术后边能在工作中实践。在工作中更多的情况是需要到了某技术,然后一遍学习一遍实践,以及事后总结。

回归主题O(∩_∩)O~

在此澄清一个概念:介绍分布式一致性算法的是Paxos,zookeeper是基于ZAB协议,即一种崩溃可恢复的原子消息广播算法。

9月份时,基于手上的这本《从PAXOS到ZOOKEEPER...》, 出现了如下几个疑惑:

  1. PAXOS是什么

  2. zookeeper和PAXOS是什么关系

  3. zookeeper的应用场景

  4. zk如何在第3点中应用场景中使用。

  5. 实现,基于Curator的Java的实现

如上 我们就以这个顺序来介绍。

 

一、PAXOS

用一句话来解释Paxos是什么,那就是消息传递的一致性算法。注意,我们是基于分布式系统的应用场景来介绍的,在分布式系统中,当机器接A收到消息a时,在各个特殊情况都有可能发生的情况下,如何让系统中的机器B,机器C...也都能准确地接收到消息a,这就是一致性算法的目的。

我们现在明确一下概念。

节点一词通常可以表达两个概念,机器节点和数据节点。具体看情景。在此所述的分布式系统中,机器A,机器B..通常就被称为节点,机器节点。

节点间的通信有两种模式:共享内存和消息传递。本文所介绍的Paxos便是基于消息传递的一致性算法。

好,那么Paxos具体是什么呢?

Leslie Lamport,莱斯利·兰伯特, 他是谁? who cares!  但是如果你用过活着知道Latex,就不是这份面孔了,→_→。。就是Latex的La,就是那个,因为嫌弃其他编辑器不好使,并且排版不漂亮的问题,自己搞出Latex的那位大牛。本人很是佩服。

这位大牛先后提出了两个场景,“拜占庭将军问题”(描述请找度娘),后因为

  1. 分布式系统处于同一个局域网,消息被篡改的可能性极小

  2. 可以校验算法解决消息不完整问题

重新提出"兼职会议"来描述一致性算法需要解决的问题。

1. 场景

“兼职会议”:小岛采用会议形式来通过法令,议员通过信使传递消息,议员和信使都是兼职的,随时可以离开会议厅且信使可能重复传递消息,这种情况下会议需要法令能够正确产生和不出现冲突。

2. PAXOS详解

三种角色:Proposer / Acceptor / Learner

2.1 提案的选定

Acceptor接收的第一个提案作为选定的提案,那么只有一个Acceptor的缺陷便是Accpetor挂了后整个系统将无法工作。

所以解决方案是有多个Acceptor可以批准提案:

当足够多的Acceptor批准同一个提案时,这个提案就被选定。足够多个Acceptor是多少个呢?包含了整个Acceptor集合的大多数成员。任意两个包含大多数成员的子集必有一个公共成员。即半数以上

P1:一个Acceptor必须批准他收到的第一个提案

P2:如果编号为M0,Value为V0的提案配(即[M0,V0])被选定了,那么所必编号M0高,且被选定的提案其Value必须也是V0

一个提案被选定需要半数以上的Acceptor批准的需求暗示一个Acceptor必须能够批准不止一个提案。需要全局编号来标识每一个被Acceptor批准的提案。

  [全局编号,Value]表示一个提案,

根据P2 在当[M0,V0]被选定时,提案者Proposer生成的提案必为[M1,V0],[M2.V0],...M2>M1>M0

 

2.1.1提案的生成

Proposer选择一个新的提案编号Mn, 然后向某个Acceptor集合的成员发送请求,要求Acceptor作出如下回应:

  1. 不再批准任何编号小于Mn的提案

  2. 如果已批准过提案,那么向Proposer反馈当前该Acceptor已经批准的编号小于Mn但为最大编号的那个提案的值。

Proposer收到来自半数以上Acceptor的响应结果,可以产生提案[Mn,Vn]

  情况1. 响应中包含提案 Vn的值来自于响应中编号最大的提案的Value值. 

  情况2. 响应中不包含提案 Vn值由Proposer任意选择

2.1.2Acceptor批准提案

一个Acceptor只要尚未响应过任何编号大于Mn的Prepare请求,那么他就可以接受这个编号为Mn的提案。

ps Acceptor可能会接收来自Proposer的两种请求,Prepare请求和Accept请求

 

2.2提案的获取

 让Learner获取提案有一下三种方案:

  1. Acceptor批准了一个提案,将该提案发送给所有Learner, 通信次数是Acceptor和Learner个数的乘积

  2. 选取主Learner, Acceptor会将得到批准的提案发送给主Learner,再由其同步给其他Learner

  3. 将主learner扩大范围即 Acceptor将批准的提案发送给一个特定的Learner集合

 

2.3 优化

2.3.1 安全性(Safety)

  1.只有被提出的提案才能被选定

  2.只有一个值被选定

  3.如果某个进程任务某个天啊被选定了,那么这个提案必须是真的被选定的那个

 

2.3.2 活性(Liveness)

两个Proposer依次提出编号递增的议案,但最终都无法被选定:

P1提出[M0,V0],Acceptor响应Prepare请求, 此时P2提出[M1,V1] ,Acceptor响应Prepare请求,同意不再批准编号小于M1的提案。

此时P1向Acceptor发出Accept请求时被拒,因此P1重新发出[M2,V2]提议后,向Accept发出Prepare请求,Acceptor响应同意不再批准编号小于M2的提案。

此时P2向Acceptor发出Accept请求时被拒...依次类推陷入死循环。

以上情况的解决方法是一个主Proposer并规定只有主Proposer能够提出议案。

 

二、zookeeper和PAXOS是什么关系

1. zookeeper相关概念

  1. 集群角色:分布式系统中最典型的集群模式是Master/Slave. 而由3~5台机器便可搭建成一个集群的zookeeper,其集群角色引入Leader,Follower和Observer. 

  2. 会话Session:客户端与服务器之间的一个TCP长连接,通过心跳检测保持有效会话,可向zk发送请求接受响应接收Wathch事件通知。在sessionTimeout内重新连接上集群中的任意一台服务器,之前的会话仍有效.

  3. 数据节点:存储数据内容,保存属性信息。  

  4. 版本:每个ZNode都会维护一个Stat的数据结构,保存三个数据版本(Znode的版本version,ZNode子节点的版本cversion,ZNode的ACL版本aversion)

  5. Watcher:事件监听器

  6. ACL:权限监控策略,Create,Read,Write,Delete,Admin .

2. ZAB协议

ZAB协议(zookeeper Atomic Broadcast)支持崩溃恢复的原子广播协议。

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

核心:定义对于会改变zookeeper服务器数据状态的事务请求的处理方式。

2.1 如何处理:

(消息广播)(基于有FIFO特性的TCP协议进行网络通信以保证信息广播过程中接收发送的顺序性)所有事务请求必须由一个全局唯一的服务器(即leader)来协调。负责将客户端事务请求转换成一个事务Proposal(提议)。并将该Proposal分发给集群中所有的Follower服务器。Leader服务器会为每一个Follower服务器各自分配一个单独的队列,然后将需要广播的事务Proposal依次放入列队中去并根据FIFO策略进行消息发送

所有的Follower服务器要么正常反馈Leader提出的事务Proposal,或者抛弃Leader服务器。

Leader服务器等待所有Follower服务器的反馈,超过半数进行正反馈,Leader会想所有Follower分发Commit消息,将Proposal进行提交。

Leader服务器崩溃退出进入崩溃恢复模式(崩溃恢复)

R1:ZAB协议需要确保那些已经在Leader服务器上提交的事务最终被所有服务器都提交。

R2:ZAB协议需要确保丢弃只在Leader服务器上被提出的事务

步骤1.所以Leader选举算法要求Leaders服务器拥有集群中所有机器最高编号(ZXID最大)的事务Proposal

步骤2.数据同步。 Leader服务器需要确保所有的Follower服务器能够接收到每一条事务Proposal,并且能够正确地将所有已经提交了的事务Proposal应用到内存数据库中去。

(如何操作)Leader为每个Follower服务器准备一个队列,并将那些没有被Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器。及每个Proposal后面紧接再发送一个Commit消息。数据同步后Leader才会将该Follower加入到真正可用的Follower列表中。

2.2 深入ZAB:

系统模型:

一组进程π,进程子集Q :Q1交Q2的集合不为空。

两个基本特性:

完整性即Pj收到消息m,说明Pi发送了消息m

前置性即m1是m2的前置消息,进程Pj收到m2前必接收到m1

可划分为3个阶段:发现(Leader),同步(数据),广播(事务)

发现(LOOKING,准Leader):

1. 所有Follower将自己最后接收的事务Proposal的epoch发送给准Leader(准Leader又是如何选出来的????)

2. 准Leader接收到过半的Follower的CEPOCH(Fp)消息,取最大的epoch值+1作为新的NEWEPOCH(e’)消息返回给这些过半的Follower。

3. Follower接收到来自准Leader L的NEWEPOCH(e’)消息后确认当前的CEPOCH(Fp)值小于e',并赋值,向准Leader反馈Ack消息。

Leader收到过半确认的Ack后,成为正式Leader,并从过半的Follower中选取一个作为初始化事务集合I(e')

同步(FOLLOWING,准Leader):

1. Leader 将e'和I(e')用NEWLEADER(e',Ie')消息的形式发送给所有Quorum中的Follower.(Quorum是什么????集群?

2. Follower收到NEWLEADER(e',Ie')消息后,若CEPOCH=e’则执行事务应用操作,任给<v,z>属于Ie',Follower都会接受<e',<v,z>>,并反馈给Leader自己已执行完事务Proposal;若CEPOCH不为e’,则说明Follower在上一轮或者更上一轮,不参与本轮同步。

3.Leader接受过半Follower对于NEWLEADER(e',Ie')消息后,向所有Follower发送Commit命令。

4.Follower接收到Commit消息后,依次处理并提交Ie''中未处理的事务。

广播(LEADING,真正的Leader):正式可以接收客户端新的请求

1.Leader接收到客户端新的事务请求后,生成Proposal。并根据ZXID的顺序依次向所有Follower发送提案<e',<v,z>>,此时的epoch(Z)=e'。

2.Follower根据接收的先后顺序依次处理Proposal,并追加到hf中去。再反馈给Leader.

3.Leader收到过半Follower针对事务Proposal<e',<v,z>>的Ack消息后,发送Commit<e',<v,z>>给所有的Follower.

4.Follower接收到Commit<e',<v,z>>消息后,便提交Proposal<e',<v,z>>

 

3. ZAB与Paxos

ZAB: 构建一个高可用的分布式数据主备系统

PAXOS: 构建一个分布式的一致性状态机系统 

 三、 zookeeper的应用场景

数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁、分布式列队

四、 如何应用

还没想好如何介绍。。。。

五、 应用

糟糕,有些虎头蛇尾  ,  真的是不想写了。。。。。。。。。。。先粘一波 后续继续

 

Curator是Netflix公司开源的一个ZooKeeper client library,用于简化ZooKeeper客户端编程。Curator包含了以下几个包:

curator-framework:对zookeeper的底层api的一些封装

curator-client:提供一些客户端的操作,例如重试策略等

curator-recipes:封装了一些高级特性,如:Cache事件监听、选举、分布式锁、分布式计数器、分布式Barrier等

使用方法:

  1. 连接zookeeper,

RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);

CuratorFramework client =

CuratorFrameworkFactory.newClient(connectionInfo,5000,3000,retryPolicy);

 

newClient静态工厂方法包含四个主要参数:

参数名

说明

connectionString

connectionString    服务器列表,格式host1:port1,host2:port2,...

retryPolicy

重试策略,内建有四种重试策略,也可以自行实现RetryPolicy接口

sessionTimeoutMs

会话超时时间,单位毫秒,默认60000ms

connectionTimeoutMs

连接创建超时时间,单位毫秒,默认60000ms

 

  1. 启动客户端

当创建会话成功后,得到client实例,可用start()启动客户端

client.start();

 

  1. 创建数据节点

Zookeeper的节点创建模式:

•   PERSISTENT:持久化

•   PERSISTENT_SEQUENTIAL:持久化并且带序列号

•   EPHEMERAL:临时

•  EPHEMERAL_SEQUENTIAL:临时并且带序列号

 

创建一个节点,指定创建模式(临时节点),附带初始化内容,并且自动递归创建父节

client.create()

.creatingParentContainersIfNeeded()

.withMode(CreateMode.EPHEMERAL)

.forPath("path","init".getBytes());

 

  1. 删除节点

删除一个节点,此方法只能删除叶子节点,否则会抛出异常。

client.delete().forPath("path");

删除一个节点,并且递归删除其所有的子节点

client.delete().deletingChildrenIfNeeded().forPath("path");

删除一个节点,强制指定版本进行删除

client.delete().withVersion(1001).forPath("path");

删除一个节点,强制保证删除

client.delete().guaranteed().forPath("path");

  1. 读取节点数据

读取一个节点的数据内容,此方法返的返回值是byte[ ];

client.getData().forPath("path");

 

  1. 更新数据节点

更新一个节点的数据内容

client.setData().forPath("path","data".getBytes());

 

  1. 检测节点存在

检查节点是否存在

client.checkExists().forPath("path");

 

  1. 监听

Path Cache用来监控一个ZNode的子节点. 当一个子节点增加, 更新,删除时, Path Cache会改变它的状态,并包含最新的子节点, 子节点的数据和状态,而状态的更变将通过PathChildrenCacheListener通知。

通过下面的构造函数创建Path Cache:

public PathChildrenCache(CuratorFramework client, String path, boolean cacheData)

想使用cache,必须调用它的start方法,使用完后调用close方法。 可以设置StartMode来实现启动的模式,

StartMode有下面几种:

1.   NORMAL:正常初始化。

2.   BUILD_INITIAL_CACHE:在调用start()之前会调用rebuild()。

3.   POST_INITIALIZED_EVENT: 当Cache初始化数据后发送一个PathChildrenCacheEvent.Type#INITIALIZED事件

public void addListener(PathChildrenCacheListener listener)可以增加listener监听。

 

posted on 2018-12-09 22:19  ZhouZzz  阅读(1048)  评论(0编辑  收藏  举报

导航