Springboot

ZooKeeper分布式专题(二) -- zookeeper应用

2019-07-16  本文已影响27人  郝小永

ZooKeeper分布式专题与Dubbo微服务入门

zookeeper基本数据类型

zookeeper应用场景

Master选举

在分布式系统中,Master往往用来协调集群中其他系统单元,具有对分布式系统状态变更的决定权,如在读写分离的应用场景中,客户端的写请求往往是由Master来处理,或者其常常处理一些复杂的逻辑并将处理结果同步给其他系统单元。利用Zookeeper的强一致性,能够很好地保证在分布式高并发情况下节点的创建一定能够保证全局唯一性,即Zookeeper将会保证客户端无法重复创建一个已经存在的数据节点。

首先创建/master_election/2019-07-05节点,客户端集群每天会定时往该节点下创建临时节点,如/master_election/2019-07-05/binding,这个过程中,只有一个客户端能够成功创建,此时其变成master,其他节点都会在节点/master_election/2019-07-05上注册一个子节点变更的Watcher,用于监控当前的Master机器是否存活,一旦发现当前Master挂了,其余客户端将会重新进行Master选举。

这也就是所谓的首脑模式,从而保证我们的集群是高可用的;

image.png
数据发布/订阅(以Dubbo注册中心为例)

数据发布/订阅系统,即配置中心。需要发布者将数据发布到Zookeeper的节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。发布/订阅一般有两种设计模式:推模式和拉模式,服务端主动将数据更新发送给所有订阅的客户端称为推模式;客户端主动请求获取最新数据称为拉模式,Zookeeper采用了推拉相结合的模式,客户端向服务端注册自己需要关注的节点,一旦该节点数据发生变更,那么服务端就会向相应的客户端推送Watcher事件通知,客户端接收到此通知后,主动到服务端获取最新的数据。

若将配置信息存放到Zookeeper上进行集中管理,在通常情况下,应用在启动时会主动到Zookeeper服务端上进行一次配置信息的获取,同时,在指定节点上注册一个Watcher监听,这样在配置信息发生变更,服务端都会实时通知所有订阅的客户端,从而达到实时获取最新配置的目的。

Dubbo是集团开源的分布式服务框架,致力于提供高性能和透明化的远程服务调用解决方案和基于服务框架展开的完整SOA服务治理方案。

其中服务自动发现是最核心的模块之一,该模块提供基于注册中心的目录服务,使服务消费方能够动态的查找服务提供方,让服务地址透明化,同时服务提供方可以平滑的对机器进行扩容和缩容,其注册中心可以基于提供的外部接口来实现各种不同类型的注册中心,例如数据库、ZK和Redis等。接下来看一下基于ZK实现的Dubbo注册中心。

image.png

/dubbo: 这是Dubbo在ZK上创建的根节点。

/dubbo/com.foo.BarService:这是服务节点,代表了Dubbo的一个服务。

/dubbo/com.foo.BarService/Providers:这是服务提供者的根节点,其子节点代表了每个服务的真正的提供者。

/dubbo/com.foo.BarService/Comsumers:这是服务消费者的根节点,其子节点代表了每一个服务的真正的消费者。

Dubbo基于ZK实现注册中心的工作流程:

服务提供者:在初始化启动的时候首先在/dubbo/com.foo.BarService/Providers节点下创建一个子节点,同时写入自己的URL地址,代表这个服务的一个提供者。

服务消费者 : 在启动的时候读图并订阅zookeeper上/dubbo/com.foo.BarService/Providers节点下的所有节点,并解析所有提供者的URL地址作为该服务类的地址列表,开始发起正常的调用。同时在Consumers节点下创建一个临时节点,写入自己的URL地址,代表自己是BarService的一个消费者

监控中心 : 监控中心是Dubbo服务治理体系的重要一部分,它需要知道一个服务的所有提供者和订阅者及变化情况。监控中心在启动的时候会通过ZK的/dubbo/com.foo.BarService节点来获取所有提供者和消费者的url地址,并注册Watcher来监听其子节点变化情况。

所有服务提供者在ZK上创建的节点都是临时节点,利用的是临时节点的生命周期和客户端会话绑定的特性,一旦提供者机器挂掉无法对外提供服务时该临时节点就会从ZK上摘除,这样服务消费者和监控中心都能感知到服务提供者的变化。

命名服务

命名服务也是分布式系统中比较常见的一类场景,被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象,其中较为常见的是一些分布式服务框架中的服务地址列表,通过使用命名服务客户端应用能够制定名字来获取资源的实体、服务地址和提供者的信息等。

上层应用使用命名服务时可能仅需要一个全局唯一的名字,类似于数据库中的唯一主键,用数据库自增id是可以的,但分库分表的情况下就无法依靠数据库的自增属性来唯一标识一条记录了。另外UUID也是一种广泛应用的ID实现方式,但如果是用UUID对服务进行命名的话就太不直观了,从字面意思根本看不出其表达的含义。下面看下用ZK如何实现全局唯一ID的生成。

之前在ZNode介绍时提过,创建节点时可以设定为SEQUENTIAL顺序节点,创建后API会返回这个节点的完整名字,利用这个特性我们就可以来生成全局唯一ID了。

image.png

所有客户端根据自己的任务类型,在指定类型的任务下创建一个顺序节点,例如“Job-”节点

节点创建完毕后会返回一个完整的节点名称,如Job-0000000001

客户端拿到这个返回值后拼接上type类型,例如type1-Job-000000001,这样就可以作为一个全局唯一的ID了

在ZK中每个数据节点都能维护一份子节点的顺序序列,当客户端对其创建一个顺序子节点时ZK会自动以后缀的形式在其子节点上添加一个序号,该场景就利用了ZK的这个特性。

分布式锁

分布式锁用于控制分布式系统之间同步访问共享资源的一种方式,可以保证不同系统访问一个或一组资源时的一致性,主要分为排它锁和共享锁。

排它锁又称为写锁或独占锁,若事务T1对数据对象O1加上了排它锁,那么在整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能再对这个数据对象进行任何类型的操作,直到T1释放了排它锁。

如果不同系统或同一系统不同机器之间共享了同一资源,那访问这些资源时通常需要一些互斥手段来保证一致性,这种情况下就需要用到分布式锁了。

使用关系型数据库是一种简单、广泛的实现方案,但大多数大型分布式系统中数据库已经是性能瓶颈了,如果再给数据库添加额外的锁会更加不堪重负;另外,使用数据库做分布式锁,当抢到锁的机器挂掉的话如何释放锁也是个头疼的问题。

接下来看下使用ZK如何实现排他锁。排他锁的核心是如何保证当前有且只有一个事务获得锁,并且锁被释放后所有等待获取锁的事务能够被通知到。

image.png

共享锁又称为读锁,若事务T1对数据对象O1加上共享锁,那么当前事务只能对O1进行读取操作,其他事务也只能对这个数据对象加共享锁,直到该数据对象上的所有共享锁都被释放。

image.png

ZooKeeper分布式专题与Dubbo微服务入门

上一篇 下一篇

猜你喜欢

热点阅读