0%

初识Zookeeper

什么是Zookeeper

ZooKeeper是一个分布式的、开源的分布式应用程序协调服务。分布式应用程序可以在此基础上实现更高级别的同步、配置维护、组和命名服务。它的设计易于编程,并使用了按照文件系统目录树结构设置数据模型。

Zookeeper特点

  1. ZooKeeper 结构清晰
    ZooKeeper允许分布式进程通过共享的空间相互协调,该空间的组成方式类似于标准文件系统。空间由数据寄存器组成(在ZooKeeper中称为znodes),它们类似于文件和目录。与设计用于存储的典型文件系统不同的是ZooKeeper数据保存在内存中,这意味着ZooKeeper可以实现高吞吐量和低延迟数。

  2. ZooKeeper 高可用
    与跟它协调的分布式服务一样,ZooKeeper本身也会在一组主机合集中进行复制。组成ZooKeeper服务的服务器相互通信。它们在内存中维护状态映像,以及持久存储中的事务日志和快照。只要大多数服务器可用,ZooKeeper服务就可用。客户端连接到单个ZooKeeper服务器后,客户端维护一个TCP连接,通过它发送请求、获取响应、获取监视事件和发送心跳。如果到服务器的TCP连接中断,客户端将切换到另一台服务器。

image.png

  1. ZooKeeper 是有序的
    ZooKeeper用反映所有ZooKeeper事务顺序的数字标记每个更新。后续操作可以使用该顺序来实现更高级别的抽象。

  2. ZooKeeper 是高性能的
    它在“读取为主”的工作负载中特别快。ZooKeeper应用程序运行在数千台机器上,当读操作比写操作更频繁时,它的性能最佳,比率约为10:1。

ZooKeeper数据模型

ZooKeeper是层次结构的空间,非常类似于分布式文件系统。名称是由斜杠(/)分隔的一系列路径元素。ZooKeeper空间中的每个节点都由路径标识。唯一的区别是,空间中的每个节点都可以具有与其关联的数据以及子级。这就像是一个允许文件也是一个目录的文件系统。

image.png

到节点的路径总是规范的、绝对的、斜线分隔的路径;没有相对引用。任何unicode字符都可以在受以下约束的路径中使用:

  • 空字符(\u0000)不能是路径名的一部分。(这会导致C绑定出现问题。)
  • 下列字符无法使用,因为它们显示不好,或呈现方式混乱:\ u0001-\u001F和\u007F-\u009F。
  • 不允许使用以下字符:ud800-uF8FF、\uFFF0-uFFFF。
  • “.”字符可以用作名称的一部分,但是”.”和”..”不能单独作为名称使用,因为ZooKeeper不使用相对路径。以下内容无效:”/a/b/./c” 或 “/a/b/../c”。
  • “zookeeper”为系统关键字

ZNodes

ZooKeeper树中的每个节点都称为znode。ZNodes维护一个统计结构(stat structure),其中包括数据更改、权限更改的版本号。统计结构也有时间戳。版本号和时间戳允许ZooKeeper验证缓存并协调更新。每次znode的数据更改时,版本号都会增加。例如,每当客户机检索数据时,它也会接收数据的版本。当客户端执行更新或删除时,它必须提供正在更改的znode的数据版本。如果它提供的版本与数据的实际版本不匹配,则更新将失败。

ZNode统计结构

ZooKeeper中每个节点的统计结构由以下字段组成:

  • czxid创建此节点时更改的zxid
  • mzxid修改此节点时更改的zxid
  • pzxid上次修改此节点子级时更改的zxid
  • ctime创建此节点时开始的时间(以毫秒为单位)。
  • mtime自上次修改此节点以来的时间(自纪元开始以毫秒为单位)。
  • version此节点的数据更改版本。
  • cversion此节点的子级更改版本。
  • aversion此节点的权限更改版本。
  • ephemeralOwner如果节点是一个临时节点,则此节点保存会话ID。如果它不是临时节点,则它将为零。
  • dataLength此节点的数据字段的长度。
  • numChildren此节点的子级数。

ZNode节点类型

  • PERSISTENT 持久化节点:
    在节点创建后,就一直存在,直到有删除操作来主动清除这个节点。

  • PERSISTENT_SEQUENTIAL 持久顺序节点:
    这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在Zookeeper中,每个父节点会为他的第一级子节点维护一份时序, 会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,Zookeeper会自动为给定节点名加上一个数字后缀,作为新的节点名。 这个数字后缀的范围是整型的最大值。 在创建节点的时候只需要传入节点 “/test_”,这样之后,Zookeeper自动会给”test_”后面补充数字。

  • EPHEMERAL 临时节点:
    和持久节点不同的是,临时节点的生命周期和客户端会话绑定。如果客户端会话失效,那么这个节点就会自动被清除掉。注意,这里提到的是会话失效,而非连接断开。另外,在临时节点下面不能创建子节点。 这里还要注意一件事,就是当你客户端会话失效后,所产生的节点也不是一下子就消失 了,也要过一段时间,大概是10秒以内。

  • EPHEMERAL_SEQUENTIAL 临时顺序节点:
    此节点是属于临时节点,不过带有顺序,客户端会话结束节点就消失。

ZNode特点

监视器(Watches)

客户端可以在节点上设置监视器。对节点的更改会触发监视器,然后清除监视器。当监视器触发时,ZooKeeper会向客户端发送通知。

数据访问

空间中每个znode都会进行原子级读取和写入。每个节点都有一个访问控制列表(ACL),用于限制谁可以执行操作。ZooKeeper并非设计为通用数据库或大型对象存储。相反,它管理协调数据。这些数据可以采用配置,状态信息,集合点等形式。各种形式的协调数据的共同属性是它们相对较小:以KB为单位。ZooKeeper客户机和服务器实现进行了健全性检查,以确保znode的数据少于1M,但数据应比平均少得多。对相对大的数据量进行操作将导致某些操作比其他操作花费更多的时间,并且会影响某些操作的延迟,因为需要更多时间才能通过网络将更多数据移动到存储介质上。如果需要大数据存储,处理此类数据的通常模式是将其存储在大容量存储系统(如NFS或HDFS)上,并在ZooKeeper中存储指向存储位置的指针。

临时节点

Zookeeper也有临时节点的概念。只要创建了ZNode的会话是活动的,这些节点就存在。会话结束时,节点将被删除。因此,临时节点不允许有子节点。

序列节点-唯一命名

创建节点时,可以在ZooKeeper路径末尾附加一个单调递增的计数器。该计数器对于父级节点是唯一的。计数器的格式为%010d,即10位数字,填充为0(零)例如:”0000000001”。

容器节点

ZooKeeper具有容器节点的概念。容器节点是特殊用途的节点,可用于诸如领导者(leader),锁(lock)等。当删除容器的最后一个子容器时,该容器将成为服务器将来要删除的候选对象。
给定此属性,您应该准备在容器节点内创建子级时获取KeeperException.NoNodeException。即,在容器节点内创建子节点时,请始终检查KeeperException.NoNodeException并在发生异常时重新创建它。

TTL 节点

创建PERSISTENTPERSISTENT_SEQUENTIAL 节点时,可以选择为节点设置TTL(以毫秒为单位)。如果节点在TTL内未修改且没有子代,它将成为将来服务器上将被删除的候选者。

TTL节点必须通过“系统”属性启用,因为默认情况下它们是禁用的。如果您尝试在没有正确设置系统属性的情况下创建TTL节点,则服务器将抛出KeeperException.UnimplementedException

安装

下载

从Apache下载最新的稳定版本。

独立部署

下载稳定的ZooKeeper版本后,将其解压缩并CD到解压目录。
要启动ZooKeeper,您需要一个配置文件。这是一个示例,创建conf/zoo.cfg

1
2
3
tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181

该文件可以被命名为任何文件,但最好将其命名为conf/zoo.cfg。更改dataDir的值以指定现有空目录。以下是每个字段的含义:

  • tickTime:ZooKeeper使用的基本时间单位(毫秒)。它用于做心跳,并且最小会话超时将是tickTime的两倍。
  • dataDir:存储内存数据库快照的位置,除非另有说明,否则存储数据库更新的事务日志。
  • clientPort:侦听客户端连接的端口

创建配置文件后,就可以启动ZooKeeper:

1
bin/zkServer.sh start

ZooKeeper使用log4j记录消息,您将看到进入控制台的日志消息或一个日志文件,具体取决于log4j配置。
此处概述的步骤以独立模式运行ZooKeeper。没有复制,因此,如果ZooKeeper进程失败,该服务将关闭。这对于大多数开发情况都很好,但是要分布式运行ZooKeeper,请参阅“ 复制模式部署”

连接到ZooKeeper

1
$ bin/zkCli.sh -server 127.0.0.1:2181

这使您可以执行简单的类似文件的操作。
连接后,您应该会看到类似以下内容的信息:

1
2
3
4
5
6
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]

在shell中,键入help以获取可以从客户端执行的命令的列表,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[zkshell: 0] help
ZooKeeper host:port cmd args
get path [watch]
ls path [watch]
set path data [version]
delquota [-n|-b] path
quit
printwatches on|off
create path data acl
stat path [watch]
listquota path
history
setAcl path acl
getAcl path
sync path
redo cmdno
addauth scheme auth
delete path [version]
deleteall path
setquota -n|-b val path

从这里,您可以尝试一些简单的命令,以了解这种简单的命令行界面。首先,首先发出list命令,如中所示ls,产生:

1
2
[zkshell: 8] ls /
[zookeeper]

接下来,通过运行创建一个新的节点 create /zk_test my_data。这将创建一个新的节点并将字符串“ my_data”与该节点关联。您应该看到:

1
2
[zkshell: 9] create /zk_test my_data
Created /zk_test

发出另一个ls /命令以查看目录的外观:

1
2
[zkshell: 11] ls /
[zookeeper, zk_test]

请注意,zk_test目录现已创建。
接下来,通过运行以下get命令来验证数据是否与znode关联:

1
2
3
4
5
6
7
8
9
10
11
12
13
[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命令来更改与zk_test相关的数据,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[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

最后,delete通过发出以下命令来删除节点:

1
2
3
4
[zkshell: 16] delete /zk_test
[zkshell: 17] ls /
[zookeeper]
[zkshell: 18]

复制模式部署ZooKeeper

以独立模式运行ZooKeeper便于评估开发和测试。但是在生产中,您应该以复制模式运行ZooKeeper。同一应用程序中的一组服务器的复制组称为quorum,并且在复制模式下,quorum中的所有服务器都具有相同配置文件的副本。

对于复制模式,至少需要三台服务器,强烈建议您使用奇数个服务器。如果只有两台服务器,则可能会出现这样的情况:如果其中一台服务器发生故障,则没有足够的计算机构成多数quorum。由于存在两个单点故障,因此两个服务器就不如本来的单个服务器稳定。

复制模式所需的conf/zoo.cfg文件类似于独立模式下使用的文件,但有一些区别。这是一个例子:

1
2
3
4
5
6
7
8
tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181
initLimit=5
syncLimit=2
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888

新条目initLimit是超时ZooKeeper用于限制quorum中的ZooKeeper服务器必须连接到领导者的时间长度。条目syncLimit限制服务器与领导者之间过时的距离。

对于这两个超时,您都可以使用tickTime指定时间单位。在此示例中,initLimit的超时为5个心跳单位,即2000毫秒*心跳,即10秒。

最后,记下每个服务器名称后的两个端口号:“2888”和“3888”。对等方使用前一个端口连接到其他对等方。这种连接是必需的,以便对等方可以进行通信,例如,商定更新顺序。ZooKeeper服务器使用另一个端口将跟随者连接到领导者。当出现新的领导者时,跟随者使用此端口打开与领导者的TCP连接。因为默认的领导者选举也使用TCP,所以我们当前需要另一个端口来进行领导者选举。

如果要在一台计算机上测试多台服务器,请为每台服务器指定服务器名称为localhost,并具有唯一的quorum和领导者端口(例如,将上面的示例修改为2888:3888, 2889:3889, 2890:3890)。当然,也需要单独的_dataDir_s和不同的_clientPort_s(在上面的复制示例中,在单个localhost上运行,您仍然需要三个配置文件)。

请注意,在一台计算机上设置多个服务器不会产生任何冗余。如果发生某些事情导致机器死机,则所有zookeeper服务器都将处于脱机状态。完全冗余要求每个服务器都有自己的计算机。它必须是完全独立的物理服务器。同一物理主机上的多个虚拟机仍然容易受到该主机完全故障的影响。