来源以及完整的文章:https://yq.aliyun.com/articles/601745?spm=a2c4e.11153940.blogcont604028.19.6daf2a38OLvUBo
注册中心需求分析及关键设计考量
接下来,让我们回归对服务发现的需求分析,结合阿里巴巴在关键场景上的实践,来一一分析,一起探讨为何说 ZooKeeper 并不是最合适的注册中心解决方案。
注册中心是 CP 还是 AP 系统?
CAP 和 BASE 理论相信读者都已经耳熟能详,其业已成了指导分布式系统及互联网应用构建的关键原则之一,在此不再赘述其理论(CAP以及BASE理论),我们直接进入对注册中心的数据一致性和可用性需求的分析。
数据一致性需求分析
注册中心最本质的功能可以看成是一个Query函数 Si = F(service-name)
,以 service-name
为查询参数,service-name
对应的服务可用的 endpoints (ip:port)
列表为返回值.
注: 后文将 service 简写为 svc。
先来看看关键数据 endpoints (ip:port)
不一致性带来的影响,即 CAP 中的 C 不满足带来的后果 :
如上图所示,如果一个 svcB 部署了10个节点 (副本/Replica),如果对于同一个服务名 svcB, 调用者 svcA 的2个节点的2次查询返回了不一致的数据,例如: S1 = { ip1,ip2,ip3...,ip9 }, S2 = { ip2,ip3,....ip10 }, 那么这次不一致带来的影响是什么?
其实影响很小,只是服务提供者svcB 的各个节点流量会有一点不均衡。
ip1和ip10相对其它8个节点{ip2...ip9},请求流量小了一点,但很明显,在分布式系统中,即使是对等部署的服务,因为请求到达的时间,硬件的状态,操作系统的调度,虚拟机的 GC 等,任何一个时间点,这些对等部署的节点状态也不可能完全一致,而流量不一致的情况下,只要注册中心在SLA承诺的时间内(例如1s内)将数据收敛到一致状态(即满足最终一致),流量将很快趋于统计学意义上的一致,所以注册中心以最终一致性的模型设计在生产实践中完全可以接受。
分区容忍及可用性需求分析
接下来我们看一下网络分区(Network Partition)情况下注册中心不可用对服务调用产生的影响,即 CAP 中的A不满足时带来的影响。
考虑一个典型的ZooKeeper三机房容灾5节点部署结构 (即2-2-1结构),如下图:
如上图,3个机房分别都有Zookeeper注册中心和应用服务集群,应用服务优先注册到同机房的Zookeeper注册中心;
当同机房的ZK注册中心不可用,应用服务svcB无法注册到同机房ZK,就会访问ZK集群的其他节点,上图由于出现了网络分区故障,机房3无法与其他机房通信,所以svcB不能注册到任何ZK上,而且应用服务svcA也无法访问任何ZK,无法调用svcB;
此时,当机房3出现网络分区(Network Partitioned)的时候,即机房3在网络上成了孤岛,我们知道虽然整体 ZooKeeper 服务是可用的,但是机房3的节点ZK5是不可写的,因为联系不上 Leader。
因为机房3节点ZK5不可写,这时候机房3的应用服务 svcB 是不可以新部署,重新启动,扩容或者缩容的,但是站在网络和服务调用的角度看,机房3的 svcA 虽然无法调用机房1和机房2的 svcB,但是与机房3的svcB之间的网络明明是 OK 的啊,为什么不让我调用本机房的服务?
现在因为注册中心自身为了保脑裂(P)下的数据一致性(C)而放弃了可用性,导致了同机房的服务之间出现了无法调用,这是绝对不允许的!可以说在实践中,注册中心不能因为自身的任何原因破坏服务之间本身的可连通性,这是注册中心设计应该遵循的铁律! 后面在注册中心客户端灾容上我们还会继续讨论。
同时我们再考虑一下这种情况下的数据不一致性,如果机房1,2,3之间都成了孤岛,那么如果每个机房的svcA都只拿到本机房的 svcB 的ip列表,也即在各机房svcB 的ip列表数据完全不一致,影响是什么?
其实没啥大影响,只是这种情况下,全都变成了同机房调用,我们在设计注册中心的时候,有时候甚至会主动利用这种注册中心的数据可以不一致性,来帮助应用主动做到同机房调用,从而优化服务调用链路 RT 的效果!
通过以上我们的阐述可以看到,在 CAP 的权衡中,注册中心的可用性比数据强一致性更宝贵,所以整体设计更应该偏向 AP,而非 CP,数据不一致在可接受范围,而P下舍弃A却完全违反了注册中心不能因为自身的任何原因破坏服务本身的可连通性的原则。
服务调用(请求响应流)链路应该是弱依赖注册中心,必须仅在服务发布,机器上下线,服务扩缩容等必要时才依赖注册中心
Service Health Check
使用 ZooKeeper 作为服务注册中心时,服务的健康检测常利用 ZooKeeper 的 Session 活性 Track机制 以及结合 Ephemeral ZNode的机制,简单而言,就是将服务的健康监测绑定在了 ZooKeeper 对于 Session 的健康监测上,或者说绑定在TCP长链接活性探测上了。
这在很多时候也会造成致命的问题,ZK 与服务提供者机器之间的TCP长链接活性探测正常的时候,该服务就是健康的么?答案当然是否定的!注册中心应该提供更丰富的健康监测方案,服务的健康与否的逻辑应该开放给服务提供方自己定义,而不是一刀切搞成了 TCP 活性检测!
健康检测的一大基本设计原则就是尽可能真实的反馈服务本身的真实健康状态,否则一个不敢被服务调用者相信的健康状态判定结果还不如没有健康检测。