Zookeeper主要用在分布式应用中实现一致性协调调度服务。它的命名空间类似传统文件系统,每个节点都以唯一的路径进行标识,不同的是,每个节点除了可以拥有子节点外,还可拥有相对性的data数据。

一、Zookeeper命名空间

上图是一个典型的Zookeeper命名空间结构,通过路径"/app1/p_1"可访问znode1节点,每个节点可存储少量数据,如状态、配置、位置信息等等,且data信息量很小,一般在byte到KB级别。节点znode维护一个状态stat结构(包括数据变化的版本号、ACL变化、时间戳),以允许缓存验证与协调更新。每当节点数据内容改变时,多一个版本号,类似HBase。客户端获取数据的同时也获取相对应的版本号。节点数据内容以原子方式读写,读操作会读取该znode的全部data数据,同样写操作也会覆盖该znode的全部data数据,不存在部分读写的情况。同时,每个节点有一个访问控制列表ACL(Access Control List)来约束访问操作,即具有权限控制。

znode存在两种:

常规的znode: 由用户显式创建和删除

ephemeral znode:临时型znode, 其生命周期伴随于创建它的session, session结束后,ZooKeeper Server会自动删除它,当然用户也可以显式的删除

 

二、Zookeeper的Watches

Zookeeper对Node的增删改查都可触发监听,每个client可对一个znode设置一个watch事件。当watch监视的数据发生变化时,会通知设置了该watch的client,即watcher。watch事件是一次性触发器,即触发一次就会被取消,该client如果还要监视该znode的变化,需要再次设置相应的watch事件。

注:

watch事件异步发送至观察者,可能导致当两次触发时间间隔太短的时候,不同的接收者收到的事件不一致

watch是一次性触发的且在获取watch事件和设置watch事件之间有延迟,所以不能可靠的观察到节点的每一次变化

客户端监视一个节点,总是先获取watch事件,再发现节点的数据变化。
watch事件的顺序对应于zookeeper服务所见的数据更新顺序

 

三、Zookeeper读写流程

读请求到来时,将直接从replicated database获取数据,replicated database是一个内存数据库,因此读写效率高

写请求到来时,所有的写请求都会先发送给一个称之为leader的server,然后由该leader广播给各个follower(server),在收到超过一半的server反馈的ack之后,认为此次写操作成功。同时,再写操作更新到内存数据库之前,会先持久化到磁盘,用于恢复。如下图所示:

 

四、在dubbo中的应用

在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的机器,提供的服务地址,远程对象等等——这些我们都可以统称他们为名字(Name)。其中较为常见的就是一些分布式服务框架中的服务地址列表。通过调用ZK提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。比如dubbo应用中:

服务提供者在启动的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。

服务消费者启动的时候,订阅/dubbo/serviceName/providersURL/dubbo/serviceName/providers目录下的提供者URL地址,并向/dubbo/{serviceName} /consumers目录下写入自己的URL地址。

注意,所有向ZK上注册的地址都是临时节点,这样就能够保证服务提供者和消费者能够自动感应资源的变化。

另外,Dubbo还有针对服务粒度的监控,方法是订阅/dubbo/${serviceName}目录下所有提供者和消费者的信息。