一文带你了解Zookeeper的注册中心流程、探活机制、Watch机制和CP模型。
往期精选:
大家好,我是楼仔!最近开始学习注册中心相关的知识,其实在之前写的 【Dubbo系列1】Dubbo与Spring的集成 这篇文章中,已经涉猎过相关内容,现在对Zookeeper的注册中心原理再深入研究一下,主要学习它的设计思想。
注册中心主要有三种角色:
最后,RPC Client 从本地缓存的服务节点列表中,基于负载均衡算法选择一台 RPC Sever 发起调用。
根据注册中心原理的描述,注册中心必须实现以下功能,偷个懒,直接贴幅图:
Zookeeper可以充当一个服务注册表(Service Registry),让多个服务提供者形成一个集群,让服务消费者通过服务注册表获取具体的服务访问地址(Ip+端口)去访问具体的服务提供者。如下图所示:
每当一个服务提供者部署后都要将自己的服务注册到zookeeper的某一路径上: /{service}/{version}/{ip:port} 。
比如我们的HelloWorldService部署到两台机器,那么Zookeeper上就会创建两条目录:
这么描述有点不好理解,我们以dubbo为例(具体示例可以参考文章【Dubbo系列1】Dubbo与Spring的集成),下图更直观:
在zookeeper中,进行服务注册,实际上就是在zookeeper中创建了一个znode节点,该节点存储了该服务的IP、端口、调用方式(协议、序列化方式)等。该节点承担着最重要的职责,它由服务提供者(发布服务时)创建,以供服务消费者获取节点中的信息,从而定位到服务提供者真正网络拓扑位置以及得知如何调用。
RPC服务注册/发现过程简述如下:
问题:第3步中“当服务提供者的某台服务器宕机或下线时”,zookeeper如何感知到呢?
zookeeper提供了“心跳检测”功能,它会定时向各个服务提供者发送一个请求(实际上建立的是一个 socket 长连接),如果长期没有响应,服务中心就认为该服务提供者已经“挂了”,并将其剔除。
比如100.100.0.237这台机器如果宕机了,那么zookeeper上的路径就会只剩/HelloWorldService/1.0.0/100.100.0.238:16888。
问题:第3步和第5步中“注册中心会将新的服务IP地址列表发送给服务消费者机器”,这步是如何实现的呢?
这个问题也是经典的生产者-消费者问题,解决的方式有两种:
Zookeeper使用的是“发布-订阅模式”,这里就要提到Zookeeper的Watch机制,整体流程如下:
上面讲的有点抽象,大白话解读一下,Zookeeper的Watch机制其实就是一种推拉结合的模式:
探讨这个问题前,我们一定需要知道什么是CAP理论。
CAP理论是分布式架构中重要理论:
关于 P 的理解,我觉得是在整个系统中某个部分,挂掉了,或者宕机了,并不影响整个系统的运作或者说使用,而可用性是,某个系统的某个节点挂了,但是并不影响系统的接受或者发出请求。
CAP 不可能都取,只能取其中2个的原因如下:
作为一个分布式协同服务,ZooKeeper非常好,但是对于Service发现服务来说就不合适了,因为对于Service发现服务来说就算是返回了包含不实的信息的结果也比什么都不返回要好。所以当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。
但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
所以说,作为注册中心,可用性的要求要高于一致性!
在 CAP 模型中,Zookeeper整体遵循一致性(CP)原则,即在任何时候对 Zookeeper 的访问请求能得到一致的数据结果,但是当机器下线或者宕机时,不能保证服务可用性。
那为什么Zookeeper不使用最终一致性(AP)模型呢?因为这个依赖Zookeeper的核心算法是ZAB,所有设计都是为了强一致性。这个对于分布式协调系统,完全没没有毛病,但是你如果将Zookeeper为分布式协调服务所做的一致性保障,用在注册中心,或者说服务发现场景,这个其实就不合适。
我们对Zookeeper的注册中心总结如下:
尽信书则不如无书,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激。