Zookeeper
原文地址:https://blog.csdn.net/mayp1/article/details/52026797https://blog.csdn.net/taurus_7c/article/details/81143830https://blog.csdn.net/gaoshan12345678910/article/details/67638657https://www.cnblog...
原文地址:
https://blog.csdn.net/mayp1/article/details/52026797
https://blog.csdn.net/taurus_7c/article/details/81143830
https://blog.csdn.net/gaoshan12345678910/article/details/67638657
https://www.cnblogs.com/felixzh/p/5869212.html
https://blog.csdn.net/weijifeng_/article/details/79775738
https://blog.csdn.net/dandandeshangni/article/details/80558383
https://blog.csdn.net/yangsnow_rain_wind/article/details/80749402
文章目录
1.简介
说明: ZooKeeper 是一个开源的分布式协调服务,由雅虎创建,是 Google Chubby 的开源实现。
分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协
调/通知、集群管理、Master 选举、配置维护,名字服务、分布式同步、分布式锁和分布式队列
等功能。
1.1 使用场景
(1)Zookeeper命名服务
在zookeeper的文件系统里创建一个目录,即有唯一的path。在我们使用tborg无法确定上游程序的部署机器时即可与下游程序约定好path,通过path即能互相探索发现。
(2)Zookeeper的配置管理
程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在把这些配置全部放到zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就好
(3)Zookeeper集群管理
所谓集群管理无在乎两点:是否有机器退出和加入、选举master。
- 是否有机器退出:所有机器约定在父目录GroupMembers下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于是,所有人都知道:它上船了。
- 新机器加入:所有机器收到通知:新兄弟目录加入,highcount又有了
- 选举master:我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。
(4) Zookeeper分布式锁
有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。
对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。
对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。
2. 基本命令与操作
2.1 基本客户端操作
(1)ls:查看当前的数据节点
(2)get:获取节点数据
[zk: localhost:2181(CONNECTED) 7] get /zookeeper #下面空行说明节点内容为空
cZxid = 0x0
ctime = Thu Jan 01 00:00:00 UTC 1970
mZxid = 0x0
mtime = Thu Jan 01 00:00:00 UTC 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: localhost:2181(CONNECTED) 8]
各项说明:
cZxid :创建节点的id
ctime : 节点的创建时间
mZxid :修改节点的id
mtime :修改节点的时间
pZxid :子节点的id
cversion : 子节点的版本
dataVersion : 当前节点数据的版本
aclVersion :权限的版本
ephemeralOwner :判断是否是临时节点
dataLength : 数据的长度
numChildren :子节点的数量
(3)stat :获得节点的更新信息
[zk: localhost:2181(CONNECTED) 8] stat /zookeeper
cZxid = 0x0
ctime = Thu Jan 01 00:00:00 UTC 1970
mZxid = 0x0
mtime = Thu Jan 01 00:00:00 UTC 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
(4)create:创建节点
基本格式:create [-s] [-e] path data acl
- 创建 持久化目录节点
[zk: localhost:2181(CONNECTED) 1] create /merryyou merryyou
Created /merryyou
#获得merryyou节点内容
[zk: localhost:2181(CONNECTED) 3] get /merryyou
merryyou
cZxid = 0x200000004
ctime = Sat Jun 02 14:20:06 UTC 2018
mZxid = 0x200000004
mtime = Sat Jun 02 14:20:06 UTC 2018
pZxid = 0x200000004
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 0
- 创建临时节点:create -e
[zk: localhost:2181(CONNECTED) 4] create -e /merryyou/temp merryyou
Created /merryyou/temp
[zk: localhost:2181(CONNECTED) 5] get /merryyou
merryyou
cZxid = 0x200000004
ctime = Sat Jun 02 14:20:06 UTC 2018
mZxid = 0x200000004
mtime = Sat Jun 02 14:20:06 UTC 2018
pZxid = 0x200000005
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 1
[zk: localhost:2181(CONNECTED) 6] get /merryyou/temp
merryyou
cZxid = 0x200000005
ctime = Sat Jun 02 14:22:24 UTC 2018
mZxid = 0x200000005
mtime = Sat Jun 02 14:22:24 UTC 2018
pZxid = 0x200000005
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x2000000d4500000
dataLength = 8
numChildren = 0
[zk: localhost:2181(CONNECTED) 7]
#断开重连之后,临时节点自动消失
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
#因为默认的心跳机制,此时查询临时节点还存在
[zk: localhost:2181(CONNECTED) 0] ls /merryyou
[temp]
#再次查询,临时节点消失
[zk: localhost:2181(CONNECTED) 1] ls /merryyou
[]
[zk: localhost:2181(CONNECTED) 2]
- 创建PERSISTENT_SEQUENTIAL类型的节点: create -s
# 创建顺序节点,顺序节点会自动累加
[zk: localhost:2181(CONNECTED) 2] create -s /merryyou/sec seq
Created /merryyou/sec0000000001
[zk: localhost:2181(CONNECTED) 3] create -s /merryyou/sec seq
Created /merryyou/sec0000000002
(5)修改节点:set path data [version]
[zk: localhost:2181(CONNECTED) 6] get /merryyou
merryyou
cZxid = 0x200000004
ctime = Sat Jun 02 14:20:06 UTC 2018
mZxid = 0x200000004
mtime = Sat Jun 02 14:20:06 UTC 2018
pZxid = 0x200000009
cversion = 4
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 8
numChildren = 2
# 修改节点内容为new-merryyou
[zk: localhost:2181(CONNECTED) 7] set /merryyou new-merryyou
cZxid = 0x200000004
ctime = Sat Jun 02 14:20:06 UTC 2018
mZxid = 0x20000000a
mtime = Sat Jun 02 14:29:23 UTC 2018
pZxid = 0x200000009
cversion = 4
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 2
#再次查询,节点内容已经修改
[zk: localhost:2181(CONNECTED) 8] get /merryyou
new-merryyou
cZxid = 0x200000004
ctime = Sat Jun 02 14:20:06 UTC 2018
mZxid = 0x20000000a
mtime = Sat Jun 02 14:29:23 UTC 2018
pZxid = 0x200000009
cversion = 4
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 12
numChildren = 2
#set 根据版本号更新 dataVersion 乐观锁
[zk: localhost:2181(CONNECTED) 9] set /merryyou test-merryyou 1
cZxid = 0x200000004
ctime = Sat Jun 02 14:20:06 UTC 2018
mZxid = 0x20000000b
mtime = Sat Jun 02 14:31:30 UTC 2018
pZxid = 0x200000009
cversion = 4
dataVersion = 2
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 13
numChildren = 2
#因为数据的版本号已经修改为2 再次使用版本号1修改节点提交错误
[zk: localhost:2181(CONNECTED) 10] set /merryyou test-merryyou 1
version No is not valid : /merryyou
(6)删除节点:delete path [version]
[zk: localhost:2181(CONNECTED) 13] delete /merryyou/sec000000000
sec0000000001 sec0000000002
[zk: localhost:2181(CONNECTED) 13] delete /merryyou/sec0000000001
[zk: localhost:2181(CONNECTED) 14] ls /merryyou
[sec0000000002]
[zk: localhost:2181(CONNECTED) 15]
#版本号操作与set类似 version
2.2 常用配置说明
2.2.1 基本配置
(1) client:监听客户端连接的端口。
(2) tickTime:基本事件单元,这个时间是作为Zookeeper服务器之间 或 客户端与服务器之间维持心跳的时间间隔,每隔tickTime时间就会发送一个心跳;最小 的session过期时间为2倍tickTime
(3)dataDir:存储内存中数据库快照的位置,如果不设置参数,更新事物的日志将被存储到默认位置。
2.2.1 高级配置
(1) dataLogdDir
这个操作让管理机器把事务日志写入“dataLogDir”所指定的目录中,而不是“dataDir”所指定的目录。这将允许使用一个专用的日志设备,帮助我们避免日志和快照的竞争。配置如下:
# the directory where the snapshot is stored
dataDir=/usr/local/zk/data
(2) maxClientCnxns
这个操作将限制连接到Zookeeper的客户端数量,并限制并发连接的数量,通过IP来区分不同的客户端。此配置选项可以阻止某些类别的Dos攻击。将他设置为零或忽略不进行设置将会取消对并发连接的限制。
例如,此时我们将maxClientCnxns的值设为1,如下所示:
# set maxClientCnxns
maxClientCnxns=1
启动Zookeeper之后,首先用一个客户端连接到Zookeeper服务器上。之后如果有第二个客户端尝试对Zookeeper进行连接,或者有某些隐式的对客户端的连接操作,将会触发Zookeeper的上述配置。
(3) minSessionTimeout和maxSessionTimeout
即最小的会话超时和最大的会话超时时间。在默认情况下,minSession=2tickTime;maxSession=20tickTime。
2.2.3 集群配置
(1) initLimit
此配置表示,允许follower(相对于Leaderer言的“客户端”)连接并同步到Leader的初始化连接时间,以tickTime为单位。当初始化连接时间超过该值,则表示连接失败。
(2) syncLimit
此配置项表示Leader与Follower之间发送消息时,请求和应答时间长度。如果follower在设置时间内不能与leader通信,那么此follower将会被丢弃。
(3) server.A=B:C:D
A:其中 A 是一个数字,表示这个是服务器的编号;
B:是这个服务器的 ip 地址;
C:Leader选举的端口;
D:Zookeeper服务器之间的通信端口。
(4) myid和zoo.cfg
除了修改 zoo.cfg 配置文件,集群模式下还要配置一个文件 myid,这个文件在 dataDir 目录下,这个文件里面就有一个数据就是 A 的值,Zookeeper 启动时会读取这个文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是那个 server。
3.Zookeeper 角色
3.1 Zookeeper角色说明
(1)leader
Leader作为整个ZooKeeper集群的主节点,负责响应所有对ZooKeeper状态变更的请求。它会将每个状态更新请求进行排序和编号,以便保证整个集群内部消息处理的FIFO。
这里补充一下ZooKeeper的请求类型。对于exists,getData,getChildren等只读请求,收到该请求的zk服务器将会在本地处理,因为由第一讲的ZAB理论可知,每个服务器看到的名字空间内容都是一致的,无所谓在哪台机器上读取数据,因此如果ZooKeeper集群的负载是读多写少,并且读请求分布得均衡的话,效率是很高的。对于create,setData,delete等有写操作的请求,则需要统一转发给leader处理,leader需要决定编号、执行操作,这个过程称为一个事务(transaction)。
事务的编号就不说了,ZAB一章中已经把zxid的格式说得很清楚,已经忘了的可以回头查阅,重点来说说事务的执行。ZooKeeper事务和关系型数据库事务相似之处是都具备原子性,即整个事务(编号+执行)要么一起成功要么一起失败。另外事务还具备幂等性,即对一个事务执行多次,结果永远都是一致的。但ZooKeeper事务不具备关系型数据库事务的回滚机制,原因是不需要,因为ZAB协议已经保证消息是严格FIFO的,并且只有一个leader实际处理事务。(回忆两阶段提交2PC,之所以需要2PC的原因,归根结底是有不止一个“主”,必须保证这么多“主”看到的结果都是一致的)
(2)Follower
Follower的逻辑就比较简单了。除了响应本服务器上的读请求外,follower还要处理leader的提议,并在leader提交该提议时在本地也进行提交。Follower处理提议的过程已经在ZAB一章中描述过了。
另外需要注意的是,leader和follower构成ZooKeeper集群的法定人数,也就是说,只有他们才参与新leader的选举、响应leader的提议。
(3)Observer
如果ZooKeeper集群的读取负载很高,或者客户端多到跨机房,可以设置一些observer服务器,以提高读取的吞吐量。Observer和Follower比较相似,只有一些小区别:首先observer不属于法定人数,即不参加选举也不响应提议;其次是observer不需要将事务持久化到磁盘,一旦observer被重启,需要从leader重新同步整个名字空间。
另外,对应client: Session 是指客户端会话,在讲解客户端会话之前,我们先来了解下客户端连接。在ZooKeeper 中,一个客户端连接是指客户端和 ZooKeeper 服务器之间的TCP长连接。
ZooKeeper 对外的服务端口默认是2181,客户端启动时,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测和服务器保持有效的会话,也能够ZooKeeper 服务器发送请求并接受响应,同时还能通过该连接接收来自服务器的 Watch 事件通知。
Session 的 SessionTimeout 值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在 SessionTimeout 规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
3.2 Zookeeper 选举过程
Leader选举是保证分布式数据一致性的关键所在。当Zookeeper集群中的一台服务器出现以下两种情况之一时,需要进入Leader选举。
(1) 服务器初始化启动。
(2) 服务器运行期间无法和Leader保持连接。
下面就两种情况进行分析讲解。
3.2.1 服务器启动时期的Leader选举
在集群启动后,机器处于Looking状态,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程如下(如果Leader已经存在,新机器发出投票选择请求后,其他服务器就会通知这个新启动的服务器,告知哪个服务器是Leader,与此同时,新的服务器会与Leader建立连接,确保自己的状态与Leader一致)
(1) 每个Server发出一个投票。由于是初始情况,Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时Server1的投票为(1, 0),Server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
(2) 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。
(3) 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:
- 优先检查ZXID。ZXID比较大的服务器优先作为Leader。
- 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器。
对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于Server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
(4) 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于Server1、Server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了Leader。
(5) 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。
3.2.2 服务器运行时期的Leader选举
在Zookeeper运行期间,Leader与非Leader服务器各司其职,即便当有非Leader服务器宕机或新加入,此时也不会影响Leader,但是一旦Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮Leader选举,其过程和启动时期的Leader选举过程基本一致。假设正在运行的有Server1、Server2、Server3三台服务器,当前Leader是Server2,若某一时刻Leader挂了,此时便开始Leader选举。选举过程如下
(1) 变更状态。Leader挂后,余下的非Observer服务器都会讲自己的服务器状态变更为LOOKING,然后开始进入Leader选举过程。
(2) 每个Server会发出一个投票。在运行期间,每个服务器上的ZXID可能不同,此时假定Server1的ZXID为123,Server3的ZXID为122;在第一轮投票中,Server1和Server3都会投自己,产生投票(1, 123),(3, 122),然后各自将投票发送给集群中所有机器。
(3) 接收来自各个服务器的投票。与启动时过程相同。
(4) 处理投票。与启动时过程相同,此时,Server1将会成为Leader。
(5) 统计投票。与启动时过程相同。
(6) 改变服务器的状态。与启动时过程相同。
3.2.3 总结
当一个服务器进入LOOKING状态就会向集群中每个服务器发送一个通知消息,该消息中包括该服务器的投票信息,投票中包含 服务器标识符(sid) 和 最近执行的事务zxis信息 ,比如,一个服务器所发送的投票信息为(1,5) ,表示该服务器的sid为1, 最近执行的事务的zxid为5,(因为群首选举的目的,zxid只有一个数字,而在其他协议中,zxid则由时间戳和计数器组成).
当一个服务器收到一个投票信息,该服务器将会根据以下规则修改自己的投票信息:
(1)将接收的voteId和voteZxid作为一个标识符,并获取当前自己投票中的zxid,用myZxid和mySid表示接收服务器自己的值.
(2)如果(voteZxid < myZxid) 或者(voteZxid = myZxid 且 voteId < mySid),保留当前的投票信息
否则,修改自己的投票信息,将voteZxid 赋值给 myZxid,将voteId赋值给muSid;
简而言之,只有最新的服务器将赢得选举,因为其拥有最近一次的zxid.
4. 数据同步与更新
Zookeeper使用Zab 协议来进行事物更新,更新集群状态。
Zab: ZooKeeper原子广播协议(ZooKeeper Atomic Broadcast protocol),用于服务器提交确认一个更新事务的提交.
下面举个例子,假设我们现在有一个活动的群首服务器,并拥有仲裁数量的追随者支持该群首的管理权,通过该协议提交一个事务,类似于一个两阶段提交:
群首向所有追随者发送一个PROPOSAL消息p.
当一个追随者收到消息p后,会响应群首一个ACK消息,通知群首其已接受该提案(proposal)
当收到仲裁数量的服务器发送确认消息后(该仲裁包括群首自己),群首就会发送消息通知追随者进行提交(COMMIT)操作.
下图说明了这一过程的具体步骤顺序,我们假设群首通过隐式方式给自己发送消息.
在应答消息之前,追随者还需要执行一些检查操作.追随者将会检查所发送的提案消息是否属于其所追随的群首,并确认群首所广播的提案消息和提交事务消失的顺序正确;
Zab保障了以下几个重要属性:
- 如果群首按顺序广播了事务T和事务T’,那么每个服务器在提交T’事务前保证事务T已经提交完成;
- 如果某个服务器按照事务T、事务T’的顺序提交事务,所有其他服务器也必然会在提交事务T’前提交事务T;
第一个属性保证事务在服务器之间的传送顺序一致,而第二个竖向地保证服务器不会跳过任何事务.假设事务为状态变更操作,每个状态变更操作又依赖前一个状态变更操作的结果,如果跳过事务就会导致结果不一致性,而两阶段提交保证了事务的顺序.
5. Client连接Server过程
使用的ZooKeeper的构造函数有三个参数构成:
Zookeeper集群的服务器地址列表
该地址是可以填写多个的,以逗号分隔。如"127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183",那客户端连接的时候到底是使用哪一个呢?先随机打乱,然后轮询着用,后面再详细介绍。
sessionTimeout
最终会引出三个时间设置:和服务器端协商后的sessionTimeout、readTimeout、connectTimeout。
服务器端使用协商后的sessionTimeout:即超过该时间后,客户端没有向服务器端发送任何请求(正常情况下客户端会每隔一段时间发送心跳请求,此时服务器端会从新计算客户端的超时时间点的),则服务器端认为session超时,清理数据。此时客户端的ZooKeeper对象就不再起作用了,需要再重新new一个新的对象了。
客户端使用connectTimeout、readTimeout分别用于检测连接超时和读取超时,一旦超时,则该客户端认为该服务器不稳定,就会从新连接下一个服务器地址。
Watcher
作为ZooKeeper对象一个默认的Watcher,用于接收一些事件通知。如和服务器连接成功的通知、断开连接的通知、Session过期的通知等。同时我们可以看到,一旦和ZooKeeper服务器连接建立成功,就会获取服务器端分配的sessionId和password,如下:
sessionId=94249128002584594
password=[B@4de3aaf6
5.1 大体连接过程概述
首先与ZooKeeper服务器建立连接,有两层连接要建立:
(1)客户端与服务器端的TCP连接;
(2)在TCP连接的基础上建立session关联;
建立TCP连接之后,客户端发送ConnectRequest请求,申请建立session关联,此时服务器端会为该客户端分配sessionId和密码,同时开启对该session是否超时的检测。
当在sessionTimeout时间内,即还未超时,此时TCP连接断开:服务器端仍然认为该sessionId处于存活状态。此时,客户端会选择下一个ZooKeeper服务器地址进行TCP连接建立,TCP连接建立完成后,拿着之前的sessionId和密码发送ConnectRequest请求,如果还未到该sessionId的超时时间,则表示自动重连成功,对客户端用户是透明的,一切都在背后默默执行,ZooKeeper对象是有效的。
如果重新建立TCP连接后,已经达到该sessionId的超时时间了(服务器端就会清理与该sessionId相关的数据):则返回给客户端的sessionTimeout时间为0,sessionid为0,密码为空字节数组。客户端接收到该数据后,会判断协商后的sessionTimeout时间是否小于等于0,如果小于等于0,则使用eventThread线程先发出一个KeeperState.Expired事件,通知相应的Watcher,然后结束EventThread线程的循环,开始走向结束。此时ZooKeeper对象就是无效的了,必须要重新new一个新的ZooKeeper对象,分配新的sessionId了。
6.Java连接Zookeeper
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
public class zkClient {
public static void main(String[] args) throws IOException, KeeperException, InterruptedException {
final CountDownLatch countDownLatch = new CountDownLatch(1);
ZooKeeper zk = new ZooKeeper("10.168.x.23:2181,10.168.x.23:2182,10.168.x.23:2183", 3000, new Watcher() {
@Override
public void process(WatchedEvent event) {
if (event.getState() == Event.KeeperState.SyncConnected) {
countDownLatch.countDown();
}
System.out.println("Watch =>" + event.getType());
}
});
countDownLatch.await();
System.out.println(zk.getState());
String node = "/app1";
Stat state = zk.exists(node, false);
if (state == null) {
System.out.println("创建节点");
String createResult = zk.create(node, "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(createResult);
}
byte[] b = zk.getData(node, false, state);
System.out.println("获取data值 =》" + new String(b));
state = zk.setData(node, "1".getBytes(), state.getVersion());
System.out.println("after update, version changed to =>" + state.getVersion());
zk.delete(node,state.getVersion());
System.out.println("delete complete");
zk.close();
}
}
7. 附录:paxos算法
https://blog.csdn.net/youyou1543724847/article/details/82316767
更多推荐
所有评论(0)