一、RPC框架中有3个重要的角色:


注册中心:保存所有服务的名字,服务提供者的IP列表,服务消费者的IP列表
服务提供者:提供跨进程服务
服务消费者:寻找到指定命名的服务并消费。

二、什么是ZooKeeper


wiki说明:

  • 主要服务于分布式系统,可以用来做:统一配置管理、统一命名服务、分布式锁、集群管理。
  • 使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper 作为一个能够解决这些问题的通用中间件就应运而生了。

三、为什么ZooKeeper能干这么多

1️⃣Wiki中其实也有提到:
ZooKeeper nodes store their data in a hierarchical name space, much like a file system or a tree data structure。
ZooKeeper的数据结构,跟Unix文件系统非常类似,可以看做是一颗树,每个节点叫做ZNode。每一个节点可以通过路径来标识,结构图如下:

2️⃣ZooKeeper这棵"树"有何特点呢?ZooKeeper的节点称之为Znode,Znode分为两种类型:

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

ZooKeeper和Redis一样,也是C/S结构(分成客户端和服务端):

3️⃣监听器-----ZooKeeper配合了监听器才能够做那么多事。

常见的监听场景有以下两项:

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


四、Zookeeper用作注册中心

简言之,Zookeeper 可以充当一个服务注册表(Service Registry),让多个服务提供者形成一个集群,让服务消费者通过服务注册表获取具体的服务访问地址(IP+端口)去访问具体的服务提供者。如图:
具体来说,zookeeper 就是个分布式文件系统,每当一个服务提供者部署后都要将自己的服务注册到 zookeeper 的某一路径上/{service}/{version}/{ip:port},比如 HelloWorldService 部署到两台机器,那么 zookeeper 上就会创建两条目录:分别为/HelloWorldService/1.0.0/100.100.0.237:16888/HelloWorldService/1.0.0/100.100.0.238:16888
如图:
在 zookeeper 中,进行服务注册,实际上就是在 zookeeper 中创建了一个 znode 节点,该节点存储了该服务的ip、端口、调用方式(协议、序列化方式)等。该节点承担着最重要的职责,它由服务提供者(发布服务时)创建,以供服务消费者获取节点中的信息,从而定位到服务提供者真正网络拓扑位置以及得知如何调用。RPC服务注册、发现过程简述如下:

  1. 服务提供者启动时,会将其服务名称,ip地址注册到配置中心。
  2. 服务消费者在第一次调用服务时,会通过注册中心找到相应服务的ip地址列表,并缓存到本地,以供后续使用。当消费者调用服务时,不会再去请求注册中心,而是直接通过负载均衡算法从ip列表中取一个服务提供者的服务器调用服务。
  3. 当服务提供者的某台服务器宕机或下线时,相应的ip会从服务提供者ip列表中移除。同时,注册中心会将新的服务ip地址列表发送给服务消费者机器,缓存在消费者本机。
  4. 当某个服务的所有服务器都下线了,那么这个服务也就下线了。
  5. 同样,当服务提供者的某台服务器上线时,注册中心会将新的服务ip地址列表发送给服务消费者机器,缓存在消费者本机。
  6. 服务提供方可以根据服务消费者的数量来作为服务下线的依据。

五、感知服务的下线&上线

Zookeeper 提供了“心跳检测”功能,它会定时向各个服务提供者发送一个请求(实际上建立的是一个 socket 长连接),如果长期没有响应,服务中心就认为该服务提供者已经“挂了”,并将其剔除。如果100.100.0.237这台机器宕机了,那么zookeeper上的路径就会只剩/HelloWorldService/1.0.0/100.100.0.238:16888

服务消费者会去监听相应路径/HelloWorldService/1.0.0,一旦路径上的数据有任务变化(增加或减少),zookeeper都会通知服务消费方,服务提供者地址列表已经发生改变,从而进行更新。

更为重要的是 zookeeper 与生俱来的容错容灾能力(比如leader选举),可以确保服务注册表的高可用性。

使用 zookeeper 作为注册中心时,客户端订阅服务时会向 zookeeper 注册自身;主要是方便对调用方进行统计、管理。但订阅时是否注册 client 不是必要行为,和不同的注册中心实现有关,例如使用 consul 时便没有注册。

六、ZooKeeper如何做到统一配置管理、统一命名服务、分布式锁、集群管理

1️⃣统一配置管理

现在有系统A、B、C,三份配置,分别是ASystem.yml、BSystem.yml、CSystem.yml。这三份配置非常类似,很多的配置项几乎都一样。此时,如果要改变其中一份配置项的信息,很可能其他两份都要改。并且,改变了配置项的信息很可能就要重启系统。于是,把ASystem.yml、BSystem.yml、CSystem.yml相同的配置项抽取出来成一份公用的配置common.yml,并且即便common.yml改了,也不需要系统A、B、C重启。

做法:
可以将common.yml这份配置放在ZooKeeper的Znode节点中,系统A、B、C监听着这个Znode节点有无变更,如果变更了,及时响应。

2️⃣统一命名服务

统一命名服务的理解其实跟域名一样,是为这某一部分的资源给它取一个名字,别人通过这个名字就可以拿到对应的资源。比如说,现在有一个域名www.w2g.com,但这个域名下有多台机器:
192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
别人访问www.w2g.com即可访问到该机器,而不是通过ip去访问。

3️⃣分布式锁

系统A、B、C都去访问/locks节点

访问的时候会创建带顺序号的临时/短暂(EPHEMERAL_SEQUENTIAL)节点。比如,系统A创建了id_000000节点,系统B创建了id_000002节点,系统C创建了id_000001节点。

接着,拿到/locks节点下的所有子节点(id_000000,id_000001,id_000002),判断自己创建的是不是最小的那个节点:

  1. 如果是,则拿到锁。
  2. 释放锁:执行完操作后,把创建的节点给删掉
  3. 如果不是,则监听比自己要小1的节点变化

说明:

  1. 系统A拿到/locks节点下的所有子节点,经过比较,发现自己(id_000000),是所有子节点最小的。所以得到锁。
  2. 系统B拿到/locks节点下的所有子节点,经过比较,发现自己(id_000002),不是所有子节点最小的。所以监听比自己小1的节点id_000001的状态。
  3. 系统C拿到/locks节点下的所有子节点,经过比较,发现自己(id_000001),不是所有子节点最小的。所以监听比自己小1的节点id_000000的状态。
  4. ……
  5. 等到系统A执行完操作以后,将自己创建的节点删除(id_000000)。通过监听,系统C发现(id_000000)节点已经删除了,发现自己已经是最小的节点了,于是顺利拿到锁。
  6. ….系统B如上

4️⃣集群状态

只要系统 A 挂了,那/groupMember/A这个节点就会删除,通过监听groupMember下的子节点,系统B和C就能够感知到系统A已经挂了。(新增也是同理)

除了能够感知节点的上下线变化,ZooKeeper 还可以实现动态选举 Master 的功能。(如果集群是主从架构模式下)

原理也很简单,如果想要实现动态选举 Master 的功能,Znode节点的类型是带顺序号的临时节点(EPHEMERAL_SEQUENTIAL)就好了。

Zookeeper 会每次选举最小编号的作为 Master,如果 Master 挂了,自然对应的 Znode 节点就会删除。然后让新的最小编号作为 Master,这样就可以实现动态选举的功能了。

七、Zookeeper的据结构模型是什么?数据节点的类型有哪些?

  1. 数据结构类似文件系统的树形结构;
  2. 节点类型有持久节点和临时节点

八、Zookeeper 如何实现分布式锁

  1. 在 zk 创建一个 lock 节点,为锁的根节点(持久性节点)
  2. 当需要访问共享资源时,先在 lock 节点下创建该共享资源的临时顺序子节点;比如共享资源为 A,创建节名字为 A + “lock” + 序号。
  3. 在创建成功之后,获取该共享资源(A)的所有节点,如果当前节点是最小节点则表示获得了锁;否则注册上一个节点的删除事件,等待当上一个节点释放锁之后,就获得了资源。
  4. 当调用完共享资源后,通过删除删除节点来释放锁。

九、如何使用zookeeper对集群的管理,负责选举出master节点,并在master节点宕机后,实现自动的选举一台slave作为master继续对外服务?

现在有一个应用,同时只允许一台 server 作为 master 节点对外提供服务,其它的 server 作为 slave 节点。

延伸问题:如何应对因网络抖动,误删了master的情况

  1. 所有的 server,都向 zk 尝试创建 master 节点,谁创建成功谁就是 master 节点(master节点必须为临时节点)。
  2. 所有的 slave 需要关注 master 节点的删除事件,如果 master 宕机了即进入新的一轮 master 选举。
  3. 延伸问题要点:每个 slave 增加判断如果上次不是 master 则延时5s在发起争夺 master。

十、Zookeeper 之 CAP

ZooKeeper 保证的是 CP。也就是选择了一致性和分区容错性,牺牲了可用性。

  1. ZooKeeper 不能保证每次服务请求的可用性。
    注:
    在极端环境下,ZooKeeper可能会丢弃一些请求,消费者程序需要重新请求才能获得结果。所以说,ZooKeeper 不能保证服务可用性。

  2. 当 Leader 挂掉的时候,集群会马上重选 Leader。选举时长在30-120秒之间,在此期间 Follower 都是 Looking 状态,是不能提供服务的,相当于集群整个就瘫痪了,所以不满足可用性。

为什么Zookeeper的选举如此耗时?
因为Zookeeper要保证各个节点中数据的一致性,它会做两类数据同步:初始化同步与更新同步。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐