本文的Zookeeper是3.6版本的
定义
Zookeeper是一个经典的分布式一致性解决方案,致力于为分布式应用提供一个高性能,高可用、且具有严格顺序访问控制能力的分布式协调存储服务。
ZooKeeper并非设计为通用数据库或大型对象存储。相反,它管理协调数据。这些数据可以采用配置,状态信息,集合点等形式。各种形式的协调数据的共同属性是它们相对较小:以千字节为单位。ZooKeeper客户端和服务器实现具有健全性检查,以确保znode的数据少于1M,但数据应比平均少得多。对相对大的数据量进行操作将导致某些操作比其他操作花费更多的时间,并且会影响某些操作的延迟,因为需要更多时间才能通过网络将更多数据移动到存储介质上。如果需要大数据存储,则处理此类数据的通常方式是将其存储在大容量存储系统上,
高性能:Zookeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求。尤其适合以读为主的场景。
高可用:Zookeeper一般以集群的方式对外提供服务,一般3-5台机器便可组成一个集群。每台机器的Zookeeper服务都会在内存中维持自身的服务状态。并且服务之间都相互保持通信。只要集群中超过一半的Zookeeper服务能够对外服务。那么整个集群就可以对外提供服务。
严格顺序访问:对于同一客户端的每个更新请求。Zookeeper都会分配一个全局唯一的递增编号。这个编号反映了所有事务操作执行的先后顺序。
原子性:所以事务的处理结果在整个集群中的所有机器上的应用情况是一致的,也就是说,要么整个集群中的所有机器都成功应用了某一个事务。要么都没有应用。不会出现集群中部分机器应用了该事务,而部分机器没有应用的情况发生。
可靠性:一旦服务端成功应用了一个事务,并完成对客户端的响应。那么该事务引起的服务端状态变化会被一致保留下来,除非有另一个事务对其进行了变更。
单一视图:无论客户端连接的是哪个Zookeeper服务器,其看的的服务端数据模型都是一致的。当客户端正常连接集群中的一台服务器时,每次请求响应都会返回所连接的服务器上所处理的最新的事务id并进行记录。当客户端由于各种原因导致与服务器之间断连时,客户端将会以自己看到代表数据版本信息的zxid一起发送给服务端,请求建立连接。服务端在收到连接建立请求时,会进行判断:自己的数据版本没有客户端之前看到的数据版本新,服务端将会拒绝连接建立。从而保证客户端不会连接到数据版本比自己之前看到的旧的服务器上。
实时性:Zookeeper仅仅保证在一定的时间段内,客户端最终一定能够从服务端读取到最新的数据状态,最终一致性。
Zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。
Zookeeper的工作机制
Zookeeper从设计模式的角度理解,是一个基于观察者模式设计的分布式服务管理框架。它负责存储我们关心的数据,然后接收观察者的注册,在数据发生变化时,Zookeeper就负责通知已经在Zookeeper上注册的那些观察者做出相应的动作。从而实现集群中类似master/slave的管理模式、配置中心等功能。
场景
- 统一命名服务
在分布式环境下,经常需要对应用服务进行统一命名,便于标识,比如用ip标记服务不易记住,用域名或者特定名字就比较容易。 - 维护统一配置信息
java编程经常会遇到需要配置文件进行配置的情况,比如数据库mysql配置,redis配置,mq配置等。我们通常把这些配置文件放置在相应的服务器上,当需要更改配置时,就去对应的服务器上修改。但随着分布式系统的兴起。可能很多服务都需要用到该配置文件。如果把配置分散到各个进行集群的服务中,当集群大到一定程度时(成百上千,上万),每当需要修改配置文件时,就要上每一台服务器上修改。这将是非常繁琐且危险的事情。因此,需要一种服务,能够快速高效且可靠地完成配置的更改等操作,并且需要保证各个配置项在每台服务器上的数据一致性。
Zookeeper提供的就可以这样的一种服务,使用Zab协议来保证数据的一致性。现在很多开源项目都使用Zookeeper来维护配置,比如dubbo使用Zookeeper来实现服务的注册中心来实现服务治理。
客户端还可以监听节点实现发布/订阅功能,每当节点(配置)被修改。就可以通知客户端来拉取最新的配置。 - 分布式锁服务
在一个分布式应用集群中,由多台服务器组成,为了提高并发度和可靠性,多台服务器运行着同一种服务。当多个服务运行时就需要协调各服务的进度。有时候需要保证其中一个服务进行某种操作时,其他服务在此期间不能进行该操作。即对该操作加锁。当该机器挂掉后,释放锁并且fail over到其他机器继续执行该服务。 - 集群管理
一个集群有时会因为软件和硬件或者网络故障等因素,出现某些服务挂掉而被移除出集群,并且还要某些服务加到集群的情况,Zookeeper会将这些服务加入/移除的情况通知给集群中其他正常工作的服务器。以及时调整存储和计算等资源的分配。 - 生成分布式唯一ID
以前单库单表型系统时,可以使用数据库字段自带的自增机制实现唯一的记录id。但是在分库分表后,就无法使用数据库字段自带的自增机制来实现了。此时可以使用Zookeeper在分布式环境下生成全局唯一ID。大致做法:每次要生成一个唯一ID时,创建一个持久的顺序的节点。创建的新节点的序号就是新的唯一id,然后把比自己小的id删除即可。 - master选举功能:
能为一些中间件集群提供master选举的功能,比如Kafka、Hadoop,hbase等。 - 负载均衡:
比如dubbo就可以利用Zookeeper实现负载均衡和注册中心。
Zookeeper的数据模型
Zookeeper的数据是以节点为单位存储的,就是一个节点存储着一个数据,节点称为znode。节点间维护着一个树状结构,类似于unix系统的目录系统。一个节点可以有多个子节点, 但是只能有一个父节点。使用路径path来定位某个节点。
- 会有一个根节点,类似于unix系统的文件根目录/,也是用正斜杠/表示。
- 对节点的访问必须使用绝对路径,不能使用相对路径。也没得来相对…。
- /aa定位的是aa节点,/aa/aa1 、/aa/aa2、 /bb 、 /bb/bb1分别访问的是aa1、aa2、bb、bb1节点。不同父节点的子节点节点名可以相同,只要全路径不一样就可以。
- Zookeeper的数据模型有点类比于unix文件系统,节点类比文件,节点数据类比文件内容,节点acl权限类比文件权限。还有其他元数据,比如创建时间,最后一次修改时间等。
节点的组成部分
一个Zookeeper节点znode大致上可以分为三个部分:
- 节点数据:就是该节点上存储的数据,类似于K/V数据模型情况下,节点就是K,全局唯一、V就表示节点上的数据。
- 节点的子节点。
- 节点的状态stat,来描述当前节点创建,修改记录、权限等元信息。
stat属性说明:
属性名 | 属性说明 |
cZxid | 数据节点创建的事务id |
cZxid | 数据节点创建的事务id。 |
ctime | 数据节点创建的时间。 |
mZxid | 数据节点最后一次更新时的事务id |
mtime | 数据节点最后一次更新的时间。 |
pZxid | 数据节点的子节点最后一次被修改时的事务id。 |
cversion | 子节点的更改次数。 |
dataVersion | 节点数据的更改次数,也就是数据的版本。类似于关系型数据库的乐观锁。 |
aclVersion | 节点ACL权限的更改次数。 |
ephemeralOwner | 如果节点是临时节点,则该属性表示创建该节点的会话SessionId。如果该节点是持久节点,该属性为0,可以使用该属性来判断节点是否为临时节点。 |
dataLength | 数据的内容长度,单位字节。 |
numChildren | 子节点的个数。 |
节点的类型
Zookeeper中的节点有四种:
临时节点:该节点的生命周期依赖于建立它们的会话。一旦服务端任务客户端连接的会话结束(可能会有一定的超时延迟),临时节点会被自动删除,当日也可以手动删除。虽然每个临时节点都只绑定到一个客户端会话,但是它们对所有客户端都是可见的。Zookeeper的临时节点不允许拥有子节点。
持久化节点:该节点的生命周期不依赖于会话。并且只有在客户端显式执行删除操作时,他们才能被删除。
容器节点(3.6新增的):ZooKeeper具有容器节点的概念。容器节点是特殊用途的节点,可用于诸如集群中的leader,分布式锁等场景。当删除容器节点的最后一个子节点时,也就是容器节点没有子节点之后,该容器节点会在未来某个时间被服务器删除。
TTL节点(3.6新增的):在创建持久化或者持久化有序节点时,可以把节点设置为TTL节点。并指定TTL的时间,单位为毫秒。如果接在在TTL时间内未修改且没有子代,该节点会在未来某个时间被服务器删除。该属性默认是禁用的,需要额外配置才能启用。
有序节点:同一父节点下的有序节点具有先后顺序关系。所谓有序节点就是创建znode时,可以要求ZooKeeper在路径末尾附加一个单调递增的计数器。该计数器对于父级znode是唯一的。计数器的格式为%010d,即10位数字,填充为0(零)(以这种格式格式化计数器以简化排序),即“0000000001“。
注意:用于存储下一个序列号的计数器是父节点维护的带符号的int(4字节),当计数器增加到2147483647以上时,计数器将溢出(导致名称 ”-2147483648“)。
安装
Zookeeper安装以及启动详解