zookeeper是一种分布式的、开源的分布式应用程序协调服务.它公开了一组简单的原语,分布式应用程序可以在此基础上实现用于同步、配置维护以及组和命名的更高级别的服务。它是为了便于编程而设计的,它使用的数据模型是按照文件系统熟悉的目录树结构设计的。它运行在Java中,并对Java和C都有绑定。
协调服务是出了名的很难得到正确的。它们特别容易出现错误,例如种族条件和死锁。动物园管理员背后的动机是减轻分布式应用程序从无到有地实现协调服务的责任。
设计目标
**ZooAdministrator允许分布式进程通过与标准文件系统类似的共享层次命名空间相互协调。命名空间由数据寄存器(用动物园管理员的说法称为znode)组成,它们与文件和目录相似。不像一个典型的文件系统是为存储而设计的,动物园管理员的数据被保存在内存中,这意味着动物园管理员可以实现高吞吐量和低延迟数。
动物园管理员的实现高度重视高性能、高可用性和严格有序的访问。动物园管理员的性能方面意味着它可以在大型分布式系统中使用。可靠性方面使它不能成为单一的故障点。严格的排序意味着复杂的同步原语可以在客户端实现。
动物园管理员被复制。就像它所协调的分布式进程一样,动物园管理员本身也打算在一组称为集成的主机上进行复制。
组成zookeeper服务的服务器必须相互了解。它们维护内存中的状态映像,以及持久存储中的事务日志和快照。只要大多数服务器可用,动物园管理员服务就可以使用。
客户端连接到单个动物园管理员服务器。客户端维护一个TCP连接,通过它发送请求、获取响应、获取监视事件和发送心跳。如果到服务器的TCP连接中断,客户端将连接到另一台服务器。
动物园管理员被命令。动物园管理员在每次更新上加盖一个数字,以反映所有动物园管理员事务的顺序。后续操作可以使用顺序来实现更高级别的抽象,例如同步原语.
动物园管理员很快。它在“以读为主”的工作负载中尤其快速。动物园管理员应用程序运行在数千台机器上,在读取比写更常见的情况下,它的性能最好,比率约为10:1。
数据模型和分层命名空间
ZooAdministrator提供的名称空间非常类似于标准文件系统的名称空间。名称是由斜杠(/)分隔的路径元素序列。动物园管理员名称空间中的每个节点都由路径标识。
动物园管理员的分级命名空间
ZooAdministrator提供的名称空间非常类似于标准文件系统的名称空间。名称是由斜杠(/)分隔的路径元素序列。动物园管理员名称空间中的每个节点都由路径标识。
动物园管理员的分级命名空间
节点和临时节点
与标准文件系统不同,动物园管理员命名空间中的每个节点都可以有与其相关的数据以及子节点。这就像有一个文件系统,允许一个文件也是一个目录.(动物园管理员被设计用来存储协调数据:状态信息、配置、位置信息等,因此存储在每个节点上的数据通常都很小,以字节到千字节为单位。)我们用这个词兹诺德为了明确说明,我们讨论的是动物园管理员数据节点。
Zode维护一个STAT结构,其中包括数据更改、ACL更改和时间戳的版本号,以允许缓存验证和协调更新。每当Zode的数据发生变化,版本号就会增加。例如,每当客户端检索数据时,它也会接收数据的版本。
存储在名称空间中每个Zode上的数据是原子式读写的。读取获取与Zode关联的所有数据字节,写替换所有数据。每个节点都有一个访问控制列表(ACL),该列表限制谁可以做什么。
动物园管理员也有临时节点的概念。只要创建Zode的会话是活动的,这些zode就存在。会话结束时,将删除Zode。
条件更新和监视
动物园管理员支持表表。客户端可以在Zode上设置一个手表。当Zode发生变化时,将触发并移除手表。当触发手表时,客户端收到一个数据包,表示Zode已更改。如果客户端和一个动物园管理员服务器之间的连接中断,客户端将收到本地通知。
新的3.6.0:客户端还可以在Zode上设置永久的递归监视,这些监视在触发时不会被移除,并且触发注册Zode上的更改,以及递归地设置任何子节点。
担保
动物园管理员是非常快速和非常简单的。但是,由于它的目标是作为构建更复杂的服务(例如同步)的基础,所以它提供了一组保证。它们是:
- 顺序一致性-来自客户端的更新将按发送顺序应用。
- 原子性-更新要么成功要么失败。没有部分结果。
- 单系统映像-客户端将看到服务的相同视图,而不管它连接到哪个服务器。也就是说,客户端将永远不会看到系统的旧视图,即使客户端失败到具有相同会话的不同服务器。
- 可靠性–一旦应用了更新,它将从那时起一直保持到客户端覆盖更新为止。
- 及时性-客户对系统的看法保证在一定的时间范围内是最新的.
简单API
动物园管理员的设计目标之一是提供一个非常简单的编程接口。因此,它只支持以下操作:
- *创造**在树中的某个位置创建一个节点
- *删除**删除节点
- *存在**测试节点是否存在于某个位置
- 获取数据:从节点读取数据
- 设定数据:将数据写入节点
- *生孩子**检索节点的子节点列表
- *同步**等待数据被传播
实施
动物园管理员部件显示动物园管理员服务的高级组件。除了请求处理器之外,组成ZooAdministrator服务的每个服务器都复制其自己的每个组件的副本。
复制的数据库是一个内存中的数据库,包含整个数据树.为了恢复,更新会记录到磁盘上,并在应用到内存数据库之前将写入序列化到磁盘。
每个动物园管理员服务器都为客户端服务。客户端连接到一个服务器来提交请求。读取请求是从每个服务器数据库的本地副本服务的。更改服务状态、写入请求的请求由协议协议处理。
作为协议协议的一部分,来自客户端的所有写请求都被转发到一个服务器,称为领队。其余的动物园管理员服务器,称为追随者,接收来自领导者的消息建议,并就消息传递达成一致。消息层负责在失败时替换领导者,并与领导者同步追随者。
动物园管理员使用自定义原子消息传递协议。由于消息传递层是原子的,动物园管理员可以保证本地副本不会发散。当领导者收到写请求时,它会计算出要应用写的时候系统的的状态是什么,并将其转换为捕获这个新状态的事务。
部署
jdk-17安装
#jdk安装 wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz #解压 tar -xf jdk-17_linux-x64_bin.tar.gz -C /usr/local sed -i '$a\export JAVA_HOME=/usr/local/jdk-17\nexport CLASSPATH=.:${JAVA_HOME}/jre/lib/rt.jar:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar\nexport PATH=$PATH:${JAVA_HOME}/bin' /etc/profile source /etc/profile java --version
zookeeper 安装
#下载zookeper 官网 http://zookeeper.apache.org/ wget https://dlcdn.apache.org/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gz tar -xf apache-zookeeper-3.7.0-bin.tar.gz -C /usr/local #配置文件zookeeper grep "^[a-Z]" /usr/local/zookeeper/conf/zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/usr/local/zookeeper/data clientPort=2181 server.2=192.168.188.17:2888:3888 server.3=192.168.188.18:2888:3888 server.4=192.168.188.7:2888:3888
zookeeper 启动&连接
/usr/local/zookeeper/bin/zkServer.sh start #stop restat status #客户端连接 /usr/local/zookeeper/bin/zkCli.sh -server 127.0.0.1:2181 一旦你连接好了,你应该看到这样的东西: Connecting to localhost:2181 log4j:WARN No appenders could be found for logger (org.apache.zookeeper.ZooKeeper). log4j:WARN Please initialize the log4j system properly. Welcome to ZooKeeper! JLine support is enabled [zkshell: 0]
在这里,您可以尝试一些简单的命令来获得这个简单的命令行接口的感觉。首先,从发出list命令开始,如ls
,屈服:
[zkshell: 8] ls /
[zookeeper]
接下来,通过运行create /zk_test my_data
。这将创建一个新的Zode,并将字符串“my_data”与该节点关联。你应该看到:
[zkshell: 9] create /zk_test my_data
Created /zk_test
再发一次ls /
命令查看目录的外观:
[zkshell: 11] ls /
[zookeeper, zk_test]
注意,现在已经创建了zk_test目录。
接下来,通过运行get
指挥,如:
[zkshell: 12] get /zk_test
my_data
cZxid = 5
ctime = Fri Jun 05 13:57:06 PDT 2009
mZxid = 5
mtime = Fri Jun 05 13:57:06 PDT 2009
pZxid = 5
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0
dataLength = 7
numChildren = 0
我们可以通过发出set
指挥,如:
[zkshell: 14] set /zk_test junk
cZxid = 5
ctime = Fri Jun 05 13:57:06 PDT 2009
mZxid = 6
mtime = Fri Jun 05 14:01:52 PDT 2009
pZxid = 5
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0
dataLength = 4
numChildren = 0
[zkshell: 15] get /zk_test
junk
cZxid = 5
ctime = Fri Jun 05 13:57:06 PDT 2009
mZxid = 6
mtime = Fri Jun 05 14:01:52 PDT 2009
pZxid = 5
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0
dataLength = 4
numChildren = 0
(注意到我们做了一个get
在设置数据之后,确实发生了变化。
最后,让我们delete
通过发出以下命令的节点:
[zkshell: 16] delete /zk_test
[zkshell: 17] ls /
[zookeeper]
[zkshell: 18]