对zookeeper的理解

zookeeper是一个开源的分布式服务。分布式应用可以基于zk实现的功能包括:数据发布/订阅、负载均衡、命名服务、集群管理、master选举、分布式锁、分布式队列等。


基本概念

角色

  在zookeeper中,主要有三种角色:

  • leader:在一个zk集群中,同一时刻只能有一个leader。zk集群通过选举,来选定一台机器为leader,leader服务器为客户端提供读写服务。
  • follower:follower服务器能够为客户端提供读服务,其参与master选举的过程。
  • observer:oberser与follower功能几乎一样,不同的是,observer不参与master选举。其配置与follower或者leader不一样的地方在于其端口后面多了一个observer:server.1=192.168.20.101:2888:3888:observer,并且设置peerType=observer。++observer可以在不影响写性能的情况下提升集群的读性能++。

会话(session)

  session指的是客户端会话。在zk中,一个客户端连接指的是客户端和zk服务器之间的长连接。zk对外的默认端口是2181,客户端启动时,首先会与服务器建立一个TCP长连接,从第一次连接建立开始,客户端会话的生命周期也就开始了,通过这个长连接,客户端能够通过心跳检测和服务器端保持有效的会话,能够向zk服务器发送请求并接受响应,同时还能接收来自服务器的watch事件通知。SessionTimeout用来设置一个客户端会话的超时时间。当服务器压力太大、网络故障或者是客户端主动断开连接等各种原因,只要客户端在SessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那之前创建的会话仍然有效。

数据节点(ZNode)

  ZNode是zookeeper数据模型中的数据单元,zk将所有数据都存储在内存中,数据模型是一个树形结构(ZNode Tree),有斜杠(/)进行路径的分割,就是一个ZNode,例如:/root/children,其中root和children都是ZNode,root是children的父级。每个ZNode上都会保存自己的数据内容。

  ZNode可以分为持久节点、临时节点、持久顺序节点和临时顺序节点。

  • 持久节点,可以使用create命令来创建。持久节点一旦被创建,除非主动删除,否则这个ZNode将一直保存在zookeeper上。例如:create /root1 hello,这样就创建了一个root1的持久节点,其数据为hello。
  • 临时节点的声明周期是跟客户端会话绑定的,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。例如:create -e /root2 world,这样就创建了一个root2的临时节点,其数据为world,当会话关闭后,这个节点就会被删除。
  • 持久顺序节点,和顺序节点的特性基本一致,唯一不同的是,每个父节点会为它的第一级子节点维护一份时序,记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,zk会自动为给定节点加上一个数字后缀,作为新的节点,数字后缀的范围是整型的最大值。例如:create -s /root3/hello hello,会输出:created /root3/hello0000000001
  • 临时顺序节点,其包含了临时节点和顺序节点的特性。创建方式:create -s -e /root4/world world,创建后的节点为world00000000001

节点属性

1
2
3
4
5
6
7
8
9
10
11
cZxid = 0x1313f3d4d9
ctime = Wed Aug 17 15:10:00 CST 2016
mZxid = 0x28b8c53eb5
mtime = Thu Aug 17 14:13:05 CST 2017
pZxid = 0x294c791a36
cversion = 13
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 28
numChildren = 11
  • cZxid:znode创建的事务id。
  • ctime:znode创建的时间。
  • mZxid:znode被修改的事务id,每次对znode的修改都会更新该值。
  • mtime:znode更新的时间。
  • pZxid:与znode的子节点有关,表示其子节点最近一次创建或者删除的时间。注意只有子节点列表变更才会更新pZxid,子节点内容变更不会影响pZxid。
  • cversion:子节点版本号,当znode子节点有变化时,cversion的值就会增加一。
  • dataVersion:数据版本号,每次对节点进行set操作,dataVersion的值都会加一。
  • aclVersion:Access Control List(访问控制)的版本号。
  • ephemeralOwner:如果该节点为临时节点,ephemeralOwner的值表示与该节点绑定的session id。如果该节点不是临时节点,ephemeralOwner的值为0。
  • dataLength:节点数据的字节数。
  • numChildren:子节点数量。

  在zk中,能改变zk服务器状态的操作被称为事务操作。一般包括数据节点的创建、删除、数据内容更新和客户端会话创建与失效等操作。对每一个事务请求,zk都会为其分配一个全局唯一的事务id,用zxid表示,通常是一个64位的数字。每个zxid对应一次更新操作,从zxid中可以间接识别出zk处理这些事务操作请求的全局顺序。

watcher

  watcher是zk中一个很重要的特性。zk允许用户在指定节点上注册一些watcher,并且在一些特定事件触发的时候,zk服务端会将事件通知到感兴趣的客户端上去。该机制是zk实现分布式协调服务的重要特性。

ACL

  zk采用ACL(Access Control Lists)策略来进行权限控制。zk定义了5中权限:

  • CREATE:创建子节点的权限。
  • READ:获取节点数据和子节点列表的权限。
  • WRITE:更新节点数据的权限。
  • DELETE:删除子节点的权限。
  • ADMIN:设置节点ACL的权限。

注意:CREATE和DELETE都是针对子节点的权限控制。


功能场景

zk是一个高可用的分布式数据管理与协调框架。基于ZAB算法的实现,该框架能够很好的保证分布式环境中数据的一致性。

数据发布/订阅

  数据发布/订阅就是通常所说的配置中心,发布者将数据发布到zk节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和动态更新。
  在系统开发中,经常会有这样的需求:系统需要使用一些通用的配置信息,例如服务器列表信息、数据库配置信息等。这些全局配置信息通常有以下三个特性。

  • 数据量通常比较小。
  • 数据内容在运行时动态变化。
  • 集群中各个服务器共享,配置一致。

  对于这样的全局配置信息就可以发布到zk上,上客户端去订阅该消息。发布\订阅系统一般有两种设计模式,分别是推(push)和拉(pull)模式。

  推:服务器端主动将数据更新发给所有订阅的客户端。拉:客户端主动发起请求来获取最新数据,通常客户端采用定时轮询拉取的方式。
  zk采用的是推拉结合的方式。如下:客户端想服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务器端就会向相应的客户端发送watcher事件通知,客户端收到这个消息后,需要主动到服务器端获取最新的数据(推拉结合)。

命名服务

  命名服务是分布式系统中比较常见的一种场景。在分布式系统中,使用命名服务,客户端应用能够根据指定名字来获取资源或者服务的地址、提供者等信息。被命名的实体通常可以是集群中的机器、提供的服务、远程对象等等。较为常见的就是一些分布式服务框架中的服务地址列表。通过在zk里创建顺序节点,能够很容易创建一个全局唯一的路径,这个路径就可以作为一个名字。

master选举

  master选举是zk最典型的应用场景。利用zk的强一致性,能够很好的保证在分布式高并发情况下节点的创建一定能够保证全局唯一性,即zk将保证客户端无法创建一个已经存在的znode。如果多个客户端请求创建一个临时节点,那么最终一定只有一个客户端请求能够创建成功。利用这个特性,能够在分布式环境中进行master选举了。

  成功创建该节点的客户端所在的机器就成为了Master。同时,其他没有成功创建该节点的客户端,都会在该节点上注册一个子节点变更的Watcher,用于监控当前Master机器是否存活,一旦发现当前的Master挂了,那么其他客户端将会重新进行Master选举。这样就实现了Master的动态选举。

分布式锁

  分布式锁是控制分布式系统之间同步访问共享资源的一种方式。分布式锁可以分为排他锁和共享锁两种。

  • 排他锁,简称X锁,又称写锁或者独占锁。如果事务T1对数据对象O1加上了排他锁,那么在整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能在对这个数据对象进行任何类型的操作,不能再对该对象加锁,直到T1释放了排他锁。排他锁的核心是如何保证当前只有一个事务获得锁,并且锁被释放后,所有正在等待获取锁的事务都能被通知到。
  • 共享锁,简称S锁,又称为读锁。如果事务T1对数据对象O1加上了共享锁,那么T1只能对O1进行读操作,其他事务也能同事对O1加共享锁(排他锁不行),直到O1上的所有共享锁都释放后O1才能被加排他锁。

  总结:++可以多个事务同时获得一个对象的共享锁,有共享锁就不能再加排他锁。++

负载均衡

分布式队列


参考:

  • 从PAXOS到ZOOKEEPER分布式一致性原理与实践