微服务注册中心
Contents
“微服务注册中心”
微服务注册中心, ZooKeeper, Eureka, Consul, Nacos
服务注册中心本质上是为了解耦服务提供者和服务消费者。对于任何一个微服务,原则上都应存在或者支持多个提供者,这是由微服务的分布式属性决定的。更进一步,为了支持弹性扩缩容特性,一个微服务的提供者的数量和分布往往是动态变化的,也是无法预先确定的。因此,原本在单体应用阶段常用的静态LB机制就不再适用了,需要引入额外的组件来管理微服务提供者的注册与发现,而这个组件就是服务注册中心。 CAP理论 CAP理论是分布式架构中重要理论
一致性(Consistency) (所有节点在同一时间具有相同的数据) 可用性(Availability) (保证每个请求不管成功或者失败都有响应) 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)
P的理解,我觉得是在整个系统中某个部分,挂掉了,或者宕机了,并不影响整个系统的运作或者说使用,
而可用性是,某个系统的某个节点挂了,但是并不影响系统的接受或者发出请求,CAP 不可能都取,只能取其中2个
原因是
如果C是第一需求的话,那么会影响A的性能,因为要数据同步,不然请求结果会有差异,但是数据同步会消耗时间,期间可用性就会降低。
如果A是第一需求,那么只要有一个服务在,就能正常接受请求,但是对与返回结果变不能保证,原因是,在分布式部署的时候,数据一致的过程不可能想切线路那么快。
再如果,同事满足一致性和可用性,那么分区容错就很难保证了,也就是单点,也是分布式的基本核心,好了,明白这些理论,就可以在相应的场景选取服务注册与发现了
Zookeeper -> CP
与 Eureka 有所不同,Apache Zookeeper 在设计时就紧遵CP原则,即任何时候对 Zookeeper 的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是 Zookeeper 不能保证每次服务请求都是可达的。
从 Zookeeper 的实际应用情况来看,在使用 Zookeeper 获取服务列表时,如果此时的 Zookeeper 集群中的 Leader 宕机了,该集群就要进行 Leader 的选举,又或者 Zookeeper 集群中半数以上服务器节点不可用 (例如有三个节点,如果节点一检测到节点三挂了 ,节点二也检测到节点三挂了,那这个节点才算是真的挂了) ,那么将无法处理该请求。所以说,Zookeeper 不能保证服务可用性。
当然,在大多数分布式环境中,尤其是涉及到数据存储的场景,数据一致性应该是首先被保证的,这也是 Zookeeper 设计紧遵CP原则的另一个原因。
但是对于服务发现来说,情况就不太一样了,针对同一个服务,即使注册中心的不同节点保存的服务提供者信息不尽相同,也并不会造成灾难性的后果。
因为对于服务消费者来说,能消费才是最重要的,消费者虽然拿到可能不正确的服务实例信息后尝试消费一下,也要胜过因为无法获取实例信息而不去消费,导致系统异常要好 (淘宝的双十一,京东的618就是紧遵AP的最好参照) 。
当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,而且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。
在云部署环境下, 因为网络问题使得zk集群失去master节点是大概率事件,虽然服务能最终恢复,但是漫长的选举事件导致注册长期不可用是不能容忍的。
zookeeper 的 CP 模型不适合注册中心
zookeeper 是一个非常优秀的项目,非常成熟,被大量的团队使用,但对于服务发现来讲,zookeeper 真的是一个错误的方案。
在 CAP 模型中,zookeeper 是 CP,意味着面对网络分区时,为了保持一致性,他是不可用的。
因为 zookeeper 是一个分布式协调系统,如果使用最终一致性 (AP)的话,将是一个糟糕的设计,他的核心算法是 Zab,所有设计都是为了一致性。
对于协调系统,这是非常正确的,但是对于服务发现,可用性是第一位的,例如发生了短暂的网络分区时,即使拿到的信息是有瑕疵的、旧的,也好过完全不可用。
zookeeper 为协调服务所做的一致性保障,用在服务发现场景是错误的。
注册中心本质上的功能就是一个查询函数:
ServiceList = F(service-name) 以 service-name 为查询参数,得到对应的可用的服务端点列表 endpoints(ip:port)。
我们假设不同的客户端得到的服务列表数据是不一致的,看看有什么后果。
一个 serviceB 部署了 10 个实例,都注册到了注册中心。
现在有 2 个服务调用者 service1 和 service2,从注册中心获取 serviceB 的服务列表,但取得的数据不一致。
s1 = { ip1,ip2 … ip9 } s2 = { ip2,ip3 … ip10 } 这个不一致带来的影响是什么?
就是 serviceB 各个实例的流量不均衡。
ip1 和 ip10 的流量是单份的,ip2-ip9 流量是双份的。
这个不均衡有什么严重影响吗?并没有,完全可以接受,而且,又不会一直这样。
所以,注册中心使用最终一致性模型 (AP)完全可以的。
现在我们看一下 CP 带来的不可用的影响。
3个机房部署 5 个 ZK 节点。
现在机房3出现网络分区了,形成了孤岛。
发生网络分区时,各个区都会开始选举 leader,那么节点数少的那个分区将会停止运行,也就是 ZK5 不可用了。
这时,serviceA 就访问不了机房1和机房2的 serviceB 了,而且连自己所在机房的 serviceB 也访问不了了。
不能访问其他机房还可以理解,不能访问自己机房的服务就理解不了了,本机房内部的网络好好的,不能因为你注册中心有问题就不能访问了吧。
因为注册中心为了保障数据一致性而放弃了可用性,导致同机房服务之间无法调用,这个是接受不了的。
所以,注册中心的可用性比数据强一致性更加重要,所以注册中心应该是偏向 AP,而不是 CP。
以上表述的是 zookeeper 的 CP 模型并不适合注册中心的需求场景。
zookeeper 的性能不适合注册中心
在大规模服务集群场景中,zookeeper 的性能也是瓶颈。
zookeeper 所有的写操作都是 leader 处理的,在大规模服务注册写请求时,压力巨大,而且 leader 是单点,无法水平扩展。
还有所有服务于 zookeeper 的长连接也是很重的负担。
zookeeper 对每一个写请求,都会写一个事务日志,同时会定期将内存数据镜像dump到磁盘,保持数据一致性和持久性。
这个动作会降低性能,而且对于注册中心来讲,是不需要的。
小结 从 CP 模型上来讲,zookeeper 并不适合注册中心高可用的需要。
从性能上来讲,zookeeper 也无法满足注册中心大规模且频繁注册写的场景。
你可能会问,zookeeper 既然这么多问题,他咋不改呢?
其实,这并不算是 zookeeper 的问题,是人家本来就不适合做注册中心,非要用他的话,肯定一堆问题。
Spring Cloud Eureka -> AP
Spring Cloud Netflix 在设计 Eureka 时就紧遵AP原则 (尽管现在2.0发布了,但是由于其闭源的原因 ,但是目前 Ereka 1.x 任然是比较活跃的) 。
Eureka Server 也可以运行多个实例来构建集群,解决单点问题,但不同于 ZooKeeper 的选举 leader 的过程,Eureka Server 采用的是Peer to Peer 对等通信。这是一种去中心化的架构,无 master/slave 之分,每一个 Peer 都是对等的。在这种架构风格中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的 serviceUrl 指向其他节点。每个节点都可被视为其他节点的副本。
在集群环境中如果某台 Eureka Server 宕机,Eureka Client 的请求会自动切换到新的 Eureka Server 节点上,当宕机的服务器重新恢复后,Eureka 会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会在节点间进行复制 (replicate To Peer) 操作,将请求复制到该 Eureka Server 当前所知的其它所有节点中。
当一个新的 Eureka Server 节点启动后,会首先尝试从邻近节点获取所有注册列表信息,并完成初始化。Eureka Server 通过 getEurekaServiceUrls() 方法获取所有的节点,并且会通过心跳契约的方式定期更新。
默认情况下,如果 Eureka Server 在一定时间内没有接收到某个服务实例的心跳 (默认周期为30秒) ,Eureka Server 将会注销该实例 (默认为90秒, eureka.instance.lease-expiration-duration-in-seconds 进行自定义配置) 。
当 Eureka Server 节点在短时间内丢失过多的心跳时,那么这个节点就会进入自我保护模式。
Eureka的集群中,只要有一台Eureka还在,就能保证注册服务可用 (保证可用性) ,只不过查到的信息可能不是最新的 (不保证强一致性) 。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
Eureka不再从注册表中移除因为长时间没有收到心跳而过期的服务; Eureka仍然能够接受新服务注册和查询请求,但是不会被同步到其它节点上 (即保证当前节点依然可用) ; 当网络稳定时,当前实例新注册的信息会被同步到其它节点中; 因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使得整个注册服务瘫痪。
Consul
Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。Consul 使用 Go 语言编写,因此具有天然可移植性 (支持Linux、windows和Mac OS X) 。
Consul 内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其他工具 (比如 ZooKeeper 等) ,使用起来也较为简单。
Consul 遵循CAP原理中的CP原则,保证了强一致性和分区容错性,且使用的是 Raft 算法,比zookeeper使用的Paxos算法更加简单。虽然保证了强一致性,但是可用性就相应下降了,例如服务注册的时间会稍长一些,因为 Consul 的 raft 协议要求必须过半数的节点都写入成功才认为注册成功 ;在leader挂掉了之后,重新选举出leader之前会导致Consul 服务不可用。
默认依赖于SDK Consul本质上属于应用外的注册方式,但可以通过SDK简化注册流程。而服务发现恰好相反,默认依赖于SDK,但可以通过Consul Template (下文会提到) 去除SDK依赖。
Consul Template Consul Template
Consul,默认服务调用者需要依赖Consul SDK来发现服务,这就无法保证对应用的零侵入性。
所幸通过Consul Template,可以定时从Consul集群获取最新的服务提供者列表并刷新LB配置 (比如nginx的upstream) ,这样对于服务调用者而言,只需要配置一个统一的服务调用地址即可。
Consul强一致性(C)带来的是:
服务注册相比Eureka会稍慢一些。因为Consul的raft协议要求必须过半数的节点都写入成功才认为注册成功 Leader挂掉时,重新选举期间整个consul不可用。保证了强一致性但牺牲了可用性。 Eureka保证高可用(A)和最终一致性
服务注册相对要快,因为不需要等注册信息replicate到其他节点,也不保证注册信息是否replicate成功 当数据出现不一致时,虽然A, B上的注册信息不完全相同,但每个Eureka节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求A查不到,但请求B就能查到。如此保证了可用性但牺牲了一致性。 其他方面,eureka就是个servlet程序,跑在servlet容器中; Consul则是go编写而成。
Nacos
Nacos是阿里开源的,Nacos 支持基于 DNS 和基于 RPC 的服务发现。在Spring Cloud中使用Nacos,只需要先下载 Nacos 并启动 Nacos server,Nacos只需要简单的配置就可以完成服务的注册发现。
Nacos除了服务的注册发现之外,还支持动态配置服务。动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
一句话概括就是Nacos = Spring Cloud注册中心 + Spring Cloud配置中心
Author -
LastMod 2021-12-28