本文来说下ZooKeeper与Eureka作为注册中心的比较
CAP定理
「CAP定理,简单来说就是分布式系统不可能同时满足Consistency 一致性、Availability 可用性、Partition Tolerance 分区容错性三个要素」
在分布式系统的发展中,影响最大的莫过于CAP定理了,是分布式系统发展的理论基石。
- 2000年,加州大学的计算机科学家 Eric Brewer提出了CAP猜想
- 2002 年,麻省理工学院的 Seth Gilbert 和 Nancy Lynch 从理论上证明了 CAP 猜想,CAP猜想成为了CAP定理
Consistency 一致性
一致性的含义为,在节点的任意时刻,访问任意节点返回的数据是一致的。即Client端写入一个数据后,Server端将数据同步到整个系统,从而保证系统的数据都相同
Availability 可用性
可用性的含义为,集群能够对用户的请求给予响应。
Partition Tolerance分区容错性
分区容错的含义为,当出现分区故障时,系统仍要对外提供服务。分布式系统中,每个服务节点都是不可靠的,当某些节点出现异常时,或者节点之间的通讯产生异常时,整个系统就产生了分区问题,分布式系统中分区问题是客观存在的。
CAP权衡
CA
系统选择CA,即不支持分区容错,只支持一致性和可用性。意味着不允许出现分区异常,网络一致处于理想状态。但是分布式系统之间网络异常是客观存在的,如果避免了P,只能把分布式系统退回到单实例系统。
CP
因为分布式系统P是客观存在的,所以我们要在CP和AP之间进行抉择。
在网络分区发生时,两个分布式节点之间无法进行通信,我们对一个节点进行的修改操作将无法同步到另外一个节点,所以数据的「一致性」将无法满足,因为两个分布式节点的数据不再保持一致。除非我们牺牲「可用性」,也就是暂停分布式节点服务,在网络分区发生时,不再提供修改数据的功能,直到网络状况完全恢复正常再继续对外提供服务
「当选择CP时,相当于放弃系统的可用性,换取一致性」。zookeeper是选择了CP的系统
在zookeeper集群中,有如下三种角色
角色 | 作用 |
---|---|
Leader | 事务请求的唯一调度者和处理者 (事务请求为除查询之外的请求) |
Follower | 处理非事务请求,参与Leader选举投票 |
Observer | 处理非事务请求,不参与选举投票 |
在Leader服务器失效时,会重新从Follower服务器中选举一个新的服务器作为Leader服务器。「在重新选举Leader服务器的过程中,事务请求会被挂起,选举完Leader服务器之后才会执行这些请求」。即为了保证一致性,放弃了系统的可用性。
AP
「当选择AP时,相当于放弃系统一致性,换取可用性」。eureka是选择了AP的系统
和zookeeper集群中有三种角色不同的是,eureka集群中每个节点扮演相同的角色,他们通过互相注册的方式来感知对方的存在,当有注册信息时,他们会同步给集群内的其他节点。
下面我从源码角度分析一下eureka是如何放弃一致性来保证可用性的(放心,不会放源码的,说一下大概思路。
eureka注册中心的信息保存在AbstractInstanceRegistry类的成员变量中
// AbstractInstanceRegistry
private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
= new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
就是一个双层map,这个双层map也很好理解。最外层是服务名,里面是一个具体的实例名
当有服务往eureka上注册时,注册信息会被保存在map中,同时会把信息同步给其他的节点。此时有可能有些节点不可用了,或者网络故障,并没有收到信息,此时集群节点内的信息可能是不一致的。
当客户端从某个eureka节点获取信息失败,或者注册失败,会自动切换到另一个eureka节点。只要有一台eureka节点可用,就能保证注册服务可用。
对比
Eureka本身是Netflix开源的一款提供服务注册和发现的产品,并且提供了相应的Java封装。在它的实现中,节点之间相互平等,部分注册中心的节点挂掉也不会对集群造成影响,即使集群只剩一个节点存活,也可以正常提供发现服务。哪怕是所有的服务注册节点都挂了,Eureka Clients(客户端)上也会缓存服务调用的信息。这就保证了我们微服务之间的互相调用足够健壮。
Zookeeper主要为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。曾经是Hadoop项目中的一个子项目,用来控制集群中的数据,目前已升级为独立的顶级项目。很多场景下也用它作为Service发现服务解决方案。
在分布式系统中有个著名的CAP定理(C-数据一致性;A-服务可用性;P-服务对网络分区故障的容错性,这三个特性在任何分布式系统中不能同时满足,最多同时满足两个)。
最后总结一下两者的区别
对比 | Zookeeper | Eureka |
---|---|---|
设计原则 | CP | AP |
优点 | 数据最终一致 | 服务高可用 |
缺点 | 选举leader过程中集群不可用 | 服务节点间的数据可能不一致 |
适用场景 | 对数据一致性要求较高 | 对注册中心服务可用性要求较高 |
Zookeeper
Zookeeper是基于CP来设计的,即任何时刻对Zookeeper的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是它不能保证每次服务请求的可用性。从实际情况来分析,在使用Zookeeper获取服务列表时,如果zookeeper正在选主,或者Zookeeper集群中半数以上机器不可用,那么将无法获得数据。所以说,Zookeeper不能保证服务可用性。
诚然,在大多数分布式环境中,尤其是涉及到数据存储的场景,数据一致性应该是首先被保证的,这也是zookeeper设计成CP的原因。但是对于服务发现场景来说,情况就不太一样了:针对同一个服务,即使注册中心的不同节点保存的服务提供者信息不尽相同,也并不会造成灾难性的后果。因为对于服务消费者来说,能消费才是最重要的——拿到可能不正确的服务实例信息后尝试消费一下,也好过因为无法获取实例信息而不去消费。(尝试一下可以快速失败,之后可以更新配置并重试)所以,对于服务发现而言,可用性比数据一致性更加重要——AP胜过CP。
Eureka
而Spring Cloud Netflix在设计Eureka时遵守的就是AP原则。Eureka Server也可以运行多个实例来构建集群,解决单点问题,但不同于ZooKeeper的选举leader的过程,Eureka Server采用的是Peer to Peer对等通信。这是一种去中心化的架构,无master/slave区分,每一个Peer都是对等的。在这种架构中,节点通过彼此互相注册来提高可用性,每个节点需要添加一个或多个有效的serviceUrl指向其他节点。每个节点都可被视为其他节点的副本。
如果某台Eureka Server宕机,Eureka Client的请求会自动切换到新的Eureka Server节点,当宕机的服务器重新恢复后,Eureka会再次将其纳入到服务器集群管理之中。当节点开始接受客户端请求时,所有的操作都会进行replicateToPeer(节点间复制)操作,将请求复制到其他Eureka Server当前所知的所有节点中。
一个新的Eureka Server节点启动后,会首先尝试从邻近节点获取所有实例注册表信息,完成初始化。Eureka Server通过getEurekaServiceUrls()方法获取所有的节点,并且会通过心跳续约的方式定期更新。默认配置下,如果Eureka Server在一定时间内没有接收到某个服务实例的心跳,Eureka Server将会注销该实例(默认为90秒,通过eureka.instance.lease-expiration-duration-in-seconds配置)。当Eureka Server节点在短时间内丢失过多的心跳时(比如发生了网络分区故障),那么这个节点就会进入自我保护模式。
什么是自我保护模式?默认配置下,如果Eureka Server每分钟收到心跳续约的数量低于一个阈值(instance的数量(60/每个instance的心跳间隔秒数)自我保护系数),并且持续15分钟,就会触发自我保护。在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。它的设计哲学前面提到过,那就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。该模式可以通过eureka.server.enable-self-preservation = false来禁用,同时eureka.instance.lease-renewal-interval-in-seconds可以用来更改心跳间隔,eureka.server.renewal-percent-threshold可以用来修改自我保护系数(默认0.85)。
本文小结
ZooKeeper基于CP,不保证高可用,如果zookeeper正在选主,或者Zookeeper集群中半数以上机器不可用,那么将无法获得数据。Eureka基于AP,能保证高可用,即使所有机器都挂了,也能拿到本地缓存的数据。作为注册中心,其实配置是不经常变动的,只有发版和机器出故障时会变。对于不经常变动的配置来说,CP是不合适的,而AP在遇到问题时可以用牺牲一致性来保证可用性,既返回旧数据,缓存数据。
所以理论上Eureka是更适合做注册中心。而现实环境中大部分项目可能会使用ZooKeeper,那是因为集群不够大,并且基本不会遇到用做注册中心的机器一半以上都挂了的情况。所以实际上也没什么大问题。