

注册中心与微服务兴起
在单体应用中,所有功能都在一个应用中,但是随着业务增长,系统功能逐渐变得庞大,单体应用在开发、部署等方面效率逐渐低下,面临开发协同越来越困难等问题。因此,对系统进行拆分变的必要。
拆分之后应用间隔离与自治能力上的欠缺,从而推动微服务的发展,微服务真正的崛起是在 2014 年,标志性事件是 Martin Fowler 与 James Lewis 合写的文章《Microservices: A Definition of This New Architectural Term》中首次提到的微服务。今天我们所了解的“微服务”就是这篇文章中定义的“微服务”。在此文中,首先给出了现代微服务的概念:“微服务是一种通过多个小型服务组合来构建单个应用的架构风格,这些服务围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言,不同的数据存储技术,运行在不同的进程之中。服务采取轻量级的通信机制和自动化的部署机制实现通信与运维。”
但是在微服务架构中,越来越多的服务被拆分出来,整个系统之间的调用关系也会越来越复杂,各个服务管理也会变的越来越困难。那么对这种数量极多、依赖关系复杂的系统如何进行管理成为了一个问题,这时需要一个注册中心进行服务治理。

注册中心原理
2.1
什么是注册中心?
注册中心主要有三种角色:

服务提供者:在启动时,向 Registry 注册自身服务,并向 Registry 定期发送心跳汇报存活状态。
服务消费者:在启动时,向 Registry 订阅服务,把 Registry 返回的服务节点列表缓存在本地内存中,并与 RPC Sever 建立连接。
服务注册中心(Registry):用于保存服务提供者的注册信息,当服务提供者节点发生变更时,Registry 会同步变更,服务消费者感知后会刷新本地内存中缓存的服务节点列表。最后,服务消费者从本地缓存的服务节点列表中,基于负载均衡算法选择一台服务提供者发起调用。
2.2
注册中心功能
注册中心要提供的功能如下:
服务注册表:注册中心的核心功能,记录各个服务提供者的信息,比如服务名、IP、端口等。维护服务订阅者信息。
服务注册、注销接口:供服务提供方进行服务注册和服务注销。
健康检查和服务摘除:提供健康信息上报接口,供注册服务上传健康信息进行服务探活。同时对于失效节点能够及时摘除并同步给服务订阅者。
服务订阅和变更通知:供服务消费者使用。
2.3
一致性协议算法
注册中心作为分布式系统关键数据存储中心,如何保证重要数据不丢失,及分布式系统中各个服务数据的一致性至关重要。如果你有一份会随时变动的数据,要确保它正确地存储于网络中的几台不同机器之上,并且要尽可能保证数据是随时可用的,你会怎么做?能够让分布式系统内部暂时容忍存在不同的状态,但最终能够保证大多数节点的状态达成一致;同时,能够让分布式系统在外部看来始终表现出整体一致的结果。这个让系统各节点不受局部的网络分区、机器崩溃、执行性能或者其他因素影响,都能最终表现出整体一致的过程,所以我们需要达成一致性的方法,注册中心的实现依赖于这种方法。
一致性协议算法主要有Paxos、Raft、ZAB,下面分别介绍。
1. Paxos
Paxos算法是Leslie Lamport在1990年提出的一种基于消息传递的一致性算法,现已是当今分布式系统最重要的理论基础,基于Paxos协议的数据同步与传统主备方式最大的区别在于:Paxos只需超过半数的副本在线且相互通信正常,就可以保证服务的持续可用,且数据不丢失。
Paxos 算法将分布式系统中的节点分为三类:
提案节点:称为 Proposer,提出对某个值进行设置操作的节点,设置值这个行为就被称之为提案(Proposal),值一旦设置成功,就是不会丢失也不可变的。
决策节点:称为 Acceptor,是应答提案的节点,决定该提案是否可被投票、是否可被接受。提案一旦得到过半数决策节点的接受,即称该提案被批准(Accept),提案被批准即意味着该值不能再被更改,也不会丢失,且最终所有节点都会接受该它。
记录节点:被称为 Learner,不参与提案,也不参与决策,只是单纯地从提案、决策节点中学习已经达成共识的提案,譬如少数派节点从网络分区中恢复时,将会进入这种状态。
使用 Paxos 算法的分布式系统里的,所有的节点都是平等的,它们都可以承担以上某一种或者多种的角色,不过为了便于确保有明确的多数派,决策节点的数量应该被设定为奇数个,且在系统初始化时,网络中每个节点都知道整个网络所有决策节点的数量、地址等信息。
Paxos 算法包括两个阶段,其中,第一阶段“准备”(Prepare)。如果某个提案节点准备发起提案,必须先向所有的决策节点广播一个许可申请(称为 Prepare 请求)。提案节点的 Prepare 请求中会附带一个全局唯一的数字 n 作为提案 ID,决策节点收到后,将会给予提案节点两个承诺与一个应答。
当提案节点收到了多数派决策节点的应答(称为 Promise 应答)后,可以开始第二阶段“批准”(Accept)过程,当每一个决策节点收到 Accept 请求时,都会在不违背以前作出的承诺的前提下,接收并持久化对当前提案 ID 和提案附带的值。如果违反此前做出的承诺,即收到的提案 ID 并不是决策节点收到过的最大的,那允许直接对此 Accept 请求不予理会。
当提案节点收到了多数派决策节点的应答(称为 Accepted 应答)后,协商结束,共识决议形成,将形成的决议发送给所有记录节点进行学习。整个过程的时序图如图所示。

2. Raft
Raft是斯坦福大学的Diego Ongaro、John Ousterhout两个人以易理解为目标设计的一致性算法,已经有了十几种语言的Raft算法实现框架,较为出名的有ETCD,Google的Kubernetes也是用了ETCD作为他的服务发现框架。Raft是Paxos的简化版,与Paxos相比,Raft强调的是易理解、易实现,Raft和Paxos一样只要保证超过半数的节点正常就能够提供服务。
Raft协议一共包含如下3类角色:
Leader(领导者):处理写请求、管理日志复制、不断发送心跳信息,表示自己还活着,不要发起新的选举,领导者由跟随者投票选举得出,每次选举,只能选出一名领导者。
Candidate(候选人):向其他节点发送请求投票的RPC消息,通知其他节点来投票,如果赢得了大多数选票,就晋升当领导者。
Follower(跟随者):接受和处理来自领导者的消息,当等待领导者心跳信息超时时,推荐自己当候选人。
核心的设计原理:谁的超时时间最短谁就有非常大的概率为领导角色。
(1)默认的情况下每个节点都是为跟随者角色。
(2)每个节点随机生成一个选举的超时时间 大概分为100-300ms,在这个超时时间内必须要等待。
(3)超时时间过后,当前节点的状态由跟随者变为竞选者角色,会给其他的节点发出选举的投票的通知,只要该竞选者有超过半数以上即可选为领导者角色。
(4)选为领导者后,会定期的给跟随者发送消息保证可以任期,跟随者不用给领导者发送消息,死了都没关系。
如下图给出状态转移图能很直观的直到这三个状态。

3. ZAB
ZAB (ZooKeeper Atomic Broadcast) ZooKeeper原子消息广播协议是ZooKeeper实现分布式数据一致性的核心算法,ZAB借鉴Paxos算法,但又不像Paxos算法那样复杂,是一种通用的分布式一致性算法,它是一种特别为ZooKeeper专门设计的支持崩溃恢复的原子广播协议。
从设计上看,ZAB协议和 Raft 很类似。ZooKeeper集群中,只有一个Leader节点,其余均为Follower节点。

整个ZAB协议一共定义了三个阶段:
(1)发现:要求zookeeper集群必须选举出一个 Leader 进程,同时 Leader 会维护一个 Follower 可用客户端列表。将来客户端可以和这些 Follower节点进行通信。
(2)同步:Leader 要负责将本身的数据与 Follower 完成同步,做到多副本存储。这样也是提现了CAP中的高可用和分区容错。Follower将队列中未处理完的请求消费完成后,写入本地事务日志中。
(3)广播:Leader 可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的 Follower。
三个阶段执行完为一个周期,在Zookeeper集群的整个生命周期中,这三个阶段会不断进行,如果Leader崩溃或因其它原因导致Leader缺失,ZAB协议会再次进入阶段一。

Zookeeper 如何实现服务注册发现
ZooKeeper 是一个针对分布式系统的、可靠的、可扩展的协调服务,它通常作为统一命名服务、统一配置管理、注册中心、分布式锁服务等角色出现。很多分布式系统都依赖与 ZooKeeper 集群实现分布式系统间的协调调度,例如:Dubbo、HDFS 2.x、HBase、Kafka 等。下面我们来看下 Zookeeper 是如何实现服务注册发现的。
3.1
ZooKeeper数据结构
1. 树状目录结构
Zookeeper是一个树状的文件目录结构,与 Unix 文件系统很类似。树中每个节点可以称作为一个ZNode,每一个ZNode都可以通过其路径唯一标识,最重要的是我们可以对每个 ZNode 进行增删改查。如下图所示:

ZNode 节点类型有如下四种:
持久节点:持久节点创建后,会一直存在,不会因创建该节点的 Client 会话失效而删除。
持久顺序节点:持久顺序节点的基本特性与持久节点一致,持久有序节点是在上面持久节点的特性上加上了有序性,有序性的意思是服务向Zookeeper注册信息时,Zookeeper 根据注册顺序给每个节点编号。
临时节点:创建临时节点的 ZooKeeper Client 会话失效之后,其创建的临时节点会被 ZooKeeper 集群自动删除。与持久节点的另一点区别是,临时节点下面不能再创建子节点,持久节点下可以存在临时节点。
临时顺序节点: 基本特性与临时节点一致,在临时节点的基础上再加上有序性。
在每个 ZNode 中都维护着一个 stat 结构,记录了该 ZNode 的元数据,其中包括版本号、操作控制列表(ACL)、时间戳和数据长度等信息。
节点监听(Wacher):节点监听是Zookeeper最重要的特性之一,客户端可以监听任意节点,节点有任何变化 Zookeeper 可以通过回调的方式通知给客户端,这样客户端不用轮询就可以及时感知节点变化。通过 ZooKeeper Client 对 ZNode 进行增删改查等基本操作,通过注册 Watcher 监听 ZNode 节点、其中的数据以及子节点的变化。一旦监听到变化,则相应的 Watcher 即被触发,相应的 ZooKeeper Client 会立即得到通知。
3.2
ZooKeeper服务注册与发现
了解了Zookeeper的这些特性,我们再来看下 Zookeeper 是如何实现服务注册和发现的。服务提供方启动成功后将服务信息注册到Zookeeper,服务信息包括实例的 IP、端口等元信息。根据服务注册特性,服务掉线后服务信息应该从注册中心中删除,客户端与Zookeeper服务端断开连接后,该节点被删除,服务注册就是利用临时有序节点的特性来实现的。
服务消费方需要调用服务提供方的接口,但因为不知道实例的 IP、端口等信息,只能从 Zookeeper 中获取调用地址列表,然后进行调用,这个过程成为服务的订阅。服务订阅是利用节点监听的特性来实现的,客户端开始监听临时有序节点,因某种原因临时有序节点被删除后,Zookeeper 通过回调将变化通知给相应 Client。

服务注册/发现过程简述如下:
服务提供者启动时,会将其服务名称、IP地址、端口等服务信息注册到注册中心。
服务消费者在第一次调用服务时,会通过注册中心找到相应的服务的IP地址列表,并缓存到本地,以供后续使用。当消费者调用服务时,不会再去请求注册中心,而是直接通过负载均衡算法从IP列表中取一个服务提供者的服务器调用服务。
当服务提供者的某台服务器宕机或下线时,相应的IP会从服务提供者IP列表中移除。同时,注册中心会将新的服务IP地址列表发送给服务消费者机器,缓存在消费者本机。
当某个服务的所有服务器都下线了,那么这个服务也就下线了。
同样,当服务提供者的某台服务器上线时,注册中心会将新的服务IP地址列表发送给服务消费者机器,缓存在消费者本机。

注册中心选型
常用的注册中心有Zookeeper、Eureka、Nacos、Consul和ETCD等,不同的注册中心各有其实现方式和特点。选择注册中心我们要根据自己需要结合各注册中心的功能、特性、可维护性、高可用性等多个方面综合考虑。

注册中心选型可以从以下几个维度来选择:
生态:各注册中心对Go语言都有支持,但是Nacos、 Consul、ETCD 社区活跃,zookeeper和Eureka社区活跃度较低。
易用性:Nacos、Eureka、Consul都有现成的管控平台,ETCD、zookeeper本身作为kv存储,没有相应的管控平台,Nacos支持中文界面,比较符合国人使用习惯。
场景支持:CP模型主要针对强一致场景,如金融类,AP模型适用于高可用场景,Nacos可以同时满足两种场景,Eureka、Consul主要满足高可用场景,Zookeepr、ETCD主要满足强一致场景,此外Nacos支持从其它注册中心同步数据,方便用户注册中心迁移。
功能完整性:所有注册中心都支持健康检查,Nacos、Consul支持的检查方式较多,满足不同应用场景,Zookeeper通过keep alive方式,能实时感知实例变化;Nacos、Consul和Eureka都支持负载均衡策略,Nacos通过Metadata selector支持更灵活的策略;此外,Nacos、Eureka都支持雪崩保护,避免因为过多的实例不健康对健康的实例造成雪崩效应。

小结
关于注册中心的对比和选型,CAP三者不可兼得,该如何取舍,选择AP,优先保证可用性和分区容错性,放弃一致性;在数据一致性要求比较高的场合,选CP,先保证一致性和分区容错性,放弃可用性,是比较常见的做法,虽然会牺牲部分用户体验。不同的技术体系也会影响选择,ETCD 和 Consul 都是Go开发的,Eureka、Nacos和 Zookeeper 都是Java开发的,可能项目属于不同的技术栈,会偏向选择对应的技术体系。具体到实际情况,我们需要结合实际情况进行取舍,选择合适自己的微服务应用注册中心。具体选择的过程中,需要结合业务场景来进行选择,也需要考虑人员的熟悉度等问题。

作者 | 鲁永锋
视觉 | 王朋玉
统筹 | 郑 洁







