什么是zookeeper?

共计 2468 个字符,预计需要花费 7 分钟才能阅读完成。

简单总结一下zookeeper的作用:

  • 使用分布式系统就无法避免对节点的管理,但是专门去处理这些问题就提高了系统的复杂性,而zookeeper就是为了解决这些问题而产生的中间件
  • 主要服务于分布式系统,可以用zookeeper来做统一配置管理,统一命名服务、分布式锁,集群管理。

zookeeper如何实现这些功能?

通过zookeeper的数据结构配合监听器就能实现,下面就这两个做简单介绍

zookeeper的数据结构

什么是zookeeper?
zookeeper结构图

Zookeeper的节点我们称之为Znode,节点带顺序编号,Znode分为两种

  • 短暂/临时(Ephemeral):当客户端和服务端断开连接后,创建的Znode就会自动删除
  • 持久(Persistent):当客户端和服务端断开连接,创建的Znode不会删除

Zookeeper是C/S架构的,分为客户端和服务端

监听器

常见的监听场景有两种

  • 监听Znode节点的数据变化
  • 监听子节点的增减变化

Zookeeper实现的功能

统一管理配置

假设我们现有三个系统A、B、C,有三份配置,分别是A.yml,B.yml,C.yml,然后这三份配置有很多配置项都是一样的。此时,改到这些共同的配置,就得每份都改,可能就需要每个系统都重启。

所以,我们需要将公用的配置抽离出一份common.yml,并且要做到common.yml改动了,也不需要各个系统都重启。

zookeeper做法:利用zookeeper的发布订阅、watch来实现。即把common.yml写到zookeeper的某个指定的节点下。应用服务系统A、系统B、系统C监听这个节点的数据变化,一旦节点数据(配置信息)发生了变化,应用服务就会收到zookeeper的通知,然后应用服务就可以从zookeeper获得新的配置信息。

什么是zookeeper?

统一命名服务

再假设一个场景,服务A(开发者小李)和服务B(开发者小王),服务A需要访问服务B的数据,但是服务A开发好了,服务B还未完成,这时候怎么办呢?

使用Zookeeper来解决:服务B开发完成部署成功后,先到Zookeeper上注册服务(就是在Zookeeper添加节点,地址为/service/B,并添加节点数据),服务A部署好了,只需要监控服务B的节点/service/B,如果发现节点数据了,那么服务A就可以访问服务B了

什么是zookeeper?

在服务A中添加代码,用于观察节点/service/B是否存在以及节点数据的变化。代码根据业务来写。

分布式锁

通过一个简单的故事就类比Zookeeper的分布式锁实现原理。

很久以前,在一个村子有一口井,水质非常的好,村民们都抢着取井里的水。井就那么一口,村里的人很多,村民为争抢取水打架斗殴,甚至头破血流。

问题总是要解决,于是村长绞尽脑汁,最终想出了一个凭号取水的方案。井边安排一个看井人,维护取水的秩序。

说起来,秩序很简单,取水之前,先取号。号排在前面的,就可以先取水。先到的排在前面,那些后到的,没有排在最前面的人,一个一个挨着,在井边排成一队。取水示意图如下 :

什么是zookeeper?

这种排队取水模型,就是一种锁的模型。排在最前面的号,拥有取水权,就是一种典型的独占锁。另外,先到先得,号排在前面的人先取到水,取水之后就轮到下一个号取水,至少,看起来挺公平的,说明它是一种公平锁。

在公平独占锁的基础上,再进一步,看看可重入锁的模型。

假定,取水时以家庭为单位,哪个家庭任何人拿到号,就可以排号取水,而且如果一个家庭有一个人拿到号,其它家人这时候过来打水不用再取号。新的排号取水示意图如下 :

什么是zookeeper?

这个就是可以重入锁的模型。只要满足条件,同一个排号,可以用来多次取水。在锁的模型中,相当于一把锁,可以被多次锁定,这就叫做可重入锁。

Zookeeper分布式锁的实现
首先,Zookeeper的每一个节点都是一个天然的顺序发号器。

在每一个节点下面创建子节点时,选择创建类型为有序(临时或持久)类型,那么在新的节点后面,会加上一个编号。

假设创建发号的节点为“/locks”,服务通过访问/locks节点来获取锁

什么是zookeeper?

然后以 /locks 为父节点,其他服务在获取锁时,都会在这个父节点下面创建相同前缀的带顺序编号的临时子节点,假设相同前缀为“/locks/id-”,生成的子节点为“/locks/id-00000000”,下一个节点为“/locks/id-00000001”,以此类推。这些节点都是临时节点。

什么是zookeeper?
创建出带编号的临时节点
其次,Zookeeper节点的传递性,可以规定编号最小的节点获得锁。每个线程在尝试占用锁之前,都要先判断一下自己编号是否最小,如果是,则获取锁。
第三,Zookeeper的节点监听机制,可以保障占有锁的方式有序且高效

每个线程在抢占锁之前,先抢号创建自己的临时Znode。释放锁的时候,就需要删除这个Znode,而其他节点处于等待通知的状态。所有节点只需要等待前一个节点的删除通知即可。第一个节点删除后,就通知第二个节点,第二个就拿到锁,以此类推,击鼓传花似的。

那如果前一个节点服务器挂了或者其他原因,没删除成功,那不是后面节点就一直处于等待的状态了?

其实,Zookeeper的内部机制是可以保证后面节点正常监听和删除锁的。因为创建的是临时节点,只要这个Znode和Zookeeper集群失去联系,就会被自动删除,那么后面的节点也能收到通知。

集群状态

那么Zookeeper是怎么感知这些节点的动态呢?

其实也是通过临时节点来管理

什么是zookeeper?
Zookeeper会给每个系统维护一个临时节点

只要系统A挂了,那么/groupMember/A 这个节点就会删除,通过监听groupMember下的子节点,系统B和系统C就能知道系统A已经挂了,新增也是如此。

除了能感知节点新增和删除变化,还能实现动态选举Master的功能(如果集群是主从架构模式)

原理很简单,Znode节点的类型是带顺序号的临时节点,Zookeeper每次会选举最小编号作为Master,如果Master挂了,对应的Znode就会删除,然后再选新的最小编号为Master,这样就实现了动态选举的功能了

正文完
 
Dustin
版权声明:本站原创文章,由 Dustin 2020-02-15发表,共计2468字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。