
这篇文章对 Zookeeper 的注册中心原理再深入研究一下主要学习它的设计思想。直接上文章目录1. 基本概念1.1 什么是注册中心注册中心主要有三种角色服务提供者RPC Server在启动时向 Registry 注册自身服务并向 Registry 定期发送心跳汇报存活状态。服务消费者RPC Client在启动时向 Registry 订阅服务把 Registry 返回的服务节点列表缓存在本地内存中并与 RPC Sever 建立连接。服务注册中心Registry用于保存 RPC Server 的注册信息当 RPC Server 节点发生变更时Registry 会同步变更RPC Client 感知后会刷新本地 内存中缓存的服务节点列表。最后RPC Client 从本地缓存的服务节点列表中基于负载均衡算法选择一台 RPC Sever 发起调用。1.2 注册中心需要实现功能根据注册中心原理的描述注册中心必须实现以下功能偷个懒直接贴幅图2. ZK 注册中心原理Zookeeper 可以充当一个服务注册表Service Registry让多个服务提供者形成一个集群让服务消费者通过服务注册表获取具体的服务访问地址Ip 端口去访问具体的服务提供者。2.1 ZK 注册流程每当一个服务提供者部署后都要将自己的服务注册到 Zookeeper 的某一路径上: /{service}/{version}/{ip:port} 。比如我们的 HelloWorldService 部署到两台机器那么 Zookeeper 上就会创建两条目录/HelloWorldService/1.0.0/100.19.20.01:16888/HelloWorldService/1.0.0/100.19.20.02:16888在 Zookeeper 中进行服务注册实际上就是在 Zookeeper 中创建了一个 znode 节点该节点存储了该服务的 IP、端口、调用方式(协议、序列化方式)等。该节点承担着最重要的职责它由服务提供者(发布服务时)创建以供服务消费者获取节点中的信息从而定位到服务提供者真正网络拓扑位置以及得知如何调用。RPC 服务注册/发现过程简述如下服务提供者启动时会将其服务名称IP 地址注册到配置中心。服务消费者在第一次调用服务时会通过注册中心找到相应的服务的 IP地址列表并缓存到本地以供后续使用。当消费者调用服务时不会再去请求注册中心而是直接通过负载均衡算法从 IP 列表中取一个服务提供者的服务器调用服务。当服务提供者的某台服务器宕机或下线时相应的 IP 会从服务提供者 IP 列表中移除。同时注册中心会将新的服务 IP 地址列表发送给服务消费者机器缓存在消费者本机。当某个服务的所有服务器都下线了那么这个服务也就下线了。同样当服务提供者的某台服务器上线时注册中心会将新的服务 IP 地址列表发送给服务消费者机器缓存在消费者本机。服务提供方可以根据服务消费者的数量来作为服务下线的依据。2.2 ZK 的心跳检测问题第 3 步中“当服务提供者的某台服务器宕机或下线时”Zookeeper 如何感知到呢Zookeeper 提供了“心跳检测”功能它会定时向各个服务提供者发送一个请求实际上建立的是一个 socket 长连接如果长期没有响应服务中心就认为该服务提供者已经“挂了”并将其剔除。比如 100.100.0.237 这台机器如果宕机了那么 Zookeeper 上的路径就会只剩 /HelloWorldService/1.0.0/100.100.0.238:16888。2.3 ZK 的 Watch 机制问题第 3 步和第 5 步中“注册中心会将新的服务 IP 地址列表发送给服务消费者机器”这步是如何实现的呢这个问题也是经典的生产者-消费者问题解决的方式有两种主动拉取策略服务的消费者定期调用注册中心提供的服务获取接口获取最新的服务列表并更新本地缓存经典案例就是 Eureka。发布-订阅模式服务消费者能够实时监控服务更新状态通常采用监听器以及回调机制。Zookeeper 使用的是“发布-订阅模式”这里就要提到 Zookeeper 的 Watch 机制整体流程如下客户端先向 ZooKeeper 服务端成功注册想要监听的节点状态同时客户端本地会存储该监听器相关的信息在 WatchManager 中当 ZooKeeper 服务端监听的数据状态发生变化时ZooKeeper 就会主动通知发送相应事件信息给相关会话客户端客户端就会在本地响应式的回调相关 Watcher 的 Handler。上面讲的有点抽象大白话解读一下Zookeeper 的 Watch 机制其实就是一种推拉结合的模式服务消费者会去监听相应路径/HelloWorldService/1.0.0一旦路径上的数据有任务变化增加或减少Zookeeper 只会发送一个事件类型和节点信息给关注的客户端而不会包括具体的变更内容所以事件本身是轻量级的这就是推的部分。收到变更通知的客户端需要自己去拉变更的数据这就是拉的部分。3. ZK 是否适合作为注册中心探讨这个问题前我们一定需要知道什么是 CAP 理论。3.1 CAP 理论CAP 理论是分布式架构中重要理论一致性(Consistency)所有节点在同一时间具有相同的数据可用性(Availability) 保证每个请求不管成功或者失败都有响应分隔容忍(Partition tolerance) 系统中任意信息的丢失或失败不会影响系统的继续运作。关于 P 的理解我觉得是在整个系统中某个部分挂掉了或者宕机了并不影响整个系统的运作或者说使用而可用性是某个系统的某个节点挂了但是并不影响系统的接受或者发出请求。CAP 不可能都取只能取其中 2 个的原因如下如果 C 是第一需求的话那么会影响A的性能因为要数据同步不然请求结果会有差异但是数据同步会消耗时间期间可用性就会降低。如果 A 是第一需求那么只要有一个服务在就能正常接受请求但是对与返回结果变不能保证原因是在分布式部署的时候数据一致的过程不可能想切线路那么快。再如果同时满足一致性和可用性那么分区容错就很难保证了也就是单点也是分布式的基本核心。3.2 ZK 作为注册中心探讨作为一个分布式协同服务ZooKeeper 非常好但是对于 Service 发现服务来说就不合适了因为对于 Service 发现服务来说就算是返回了包含不实的信息的结果也比什么都不返回要好。所以当向注册中心查询服务列表时我们可以容忍注册中心返回的是几分钟以前的注册信息但不能接受服务直接 down 掉不可用。但是 zk 会出现这样一种情况当 master 节点因为网络故障与其他节点失去联系时剩余节点会重新进行 leader 选举。问题在于选举 leader 的时间太长30 ~ 120s且选举期间整个zk集群都是不可用的这就导致在选举期间注册服务瘫痪。在云部署的环境下因网络问题使得 zk 集群失去 master 节点是较大概率会发生的事虽然服务能够最终恢复但是漫长的选举时间导致的注册长期不可用是不能容忍的。所以说作为注册中心可用性的要求要高于一致性在 CAP 模型中Zookeepe 整体遵循一致性CP原则即在任何时候对 Zookeeper 的访问请求能得到一致的数据结果但是当机器下线或者宕机时不能保证服务可用性。那为什么 Zookeeper 不使用最终一致性AP模型呢因为这个依赖Zookeeper 的核心算法是 ZAB所有设计都是为了强一致性。这个对于分布式协调系统完全没没有毛病但是你如果将 Zookeeper为分布式协调服务所做的一致性保障用在注册中心或者说服务发现场景这个其实就不合适。4. 小节我们对 Zookeeper 的注册中心总结如下Zookeeper 的心跳检测可以自动探测服务提供者机器的宕机或下线Zookeeper 的 Watch 机制可以将变更的注册列表推给服务消费者Zookeeper 是 CP 模型不太适合作为注册中心。不过网上也有说Zookeeper 目前已经支持 AP准确说是 AP Base 最终一致性可以和我一起讨论哈。