服务发现 Zookeeper 对比 Eureka

ZooKeeper:分布式协调服务(a service for coordinating processes of distributed applications

  • Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。
  • 为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等)是个伟大的开源项目
  • 很成熟,有相当大的社区来支持它的发展,而且在生产环境得到了广泛的使用
  • 并不适合用来做Service发现服务解决方案则

在分布式系统领域著名的CAP定理:

  • 一致性(consistency)
  • 可用性(Availability)
  • 分区容错(partition-tolerance)

这三个特性在任何分布式系统中不能同时满足,最多同时满足两个。 ZooKeeper是个CP的,即任何时刻对ZooKeeper的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性;但是它不能保证每次服务请求的可用性(注:就是在极端环境下,ZooKeeper可能会丢弃一些请求,消费者程序需要重新请求才能获得结果)。ZooKeeper是分布式协调服务,它的职责是保证数据(注:配置数据,状态数据)在其管辖下的所有服务之间保持同步、一致;所以就不难理解为什么ZooKeeper被设计成CP而不是AP特性的了。作为ZooKeeper的核心实现算法Zab,就是解决了分布式系统下数据如何在多个服务之间保持同步问题的。

作为一个分布式协同服务,ZooKeeper非常好,但是对于Service发现服务来说就并不合适了,因为:

  • 对于Service发现服务来说就算是返回了包含不实的信息的结果也比什么都不返回要好
  • 对于Service发现服务而言,宁可返回某服务5分钟之前在哪几个服务器上可用的信息,也不能因为暂时的网络故障而找不到可用的服务器,而不返回任何结果。ZooKeeper本身并没有正确的处理网络分割的问题
  • 在云端,网络分割问题跟其他类型的故障一样的确会发生

所以最好提前对这个问题做好100%的准备。就像Jepsn在ZooKeeper网站上发布的博客中所说:在ZooKeeper中,如果在同一个网络分区(partition)的节点数(nodes)数达不到ZooKeeper选取Leader节点的“法定人数”时,它们就会从ZooKeeper中断开,当然同时也就不能提供Service发现服务了。所以说,ZooKeeper并不适合来做Service发现服务!

如果给ZooKeeper加上客户端缓存(注:给ZooKeeper节点配上本地缓存)或者其他类似技术的话可以缓解ZooKeeper因为网络故障造成节点同步信息错误的问题。 Pinterest与Airbnb公司就使用了这个方法来防止ZooKeeper故障发生。这种方式可以从表面上解决这个问题,具体地说,当部分或者所有节点跟ZooKeeper断开的情况下,每个节点还可以从本地缓存中获取到数据;但是,即便如此,ZooKeeper下所有节点不可能保证任何时候都能缓存所有的服务注册信息。如果ZooKeeper下所有节点都断开了,或者集群中出现了网络分割的故障(注:由于交换机故障导致交换机底下的子网间不能互访);那么ZooKeeper会将它们都从自己管理范围中剔除出去,外界就不能访问到这些节点了,即便这些节点本身是“健康”的,可以正常提供服务的;所以导致到达这些节点的服务请求被丢失了。(注:这也是为什么ZooKeeper不满足CAP中A的原因)

更深层次的原因是,ZooKeeper是按照CP原则构建的,也就是说它能保证每个节点的数据保持一致,而为ZooKeeper加上缓存的做法的目的是为了让ZooKeeper变得更加可靠(available);这相当于你在一个已有的CP系统上强制栓了一个AP的系统,这在本质上就行不通的!一个Service发现服务应该从一开始就被设计成高可用的才行!

如果抛开CAP原理不管,正确的设置与维护ZooKeeper服务就非常的困难;错误会经常发生,导致很多工程被建立只是为了减轻维护ZooKeeper的难度。这些错误不仅存在与客户端而且还存在于ZooKeeper服务器本身。

Eureka的成功

Eureka,是一个开源的服务发现解决方案,由Netflix公司开发。(注:Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持)Eureka一开始就被设计成高可用与可伸缩的Service发现服务,这两个特点也是Netflix公司开发所有平台的两个特色。

  • 在Eureka平台中,如果某台服务器宕机,Eureka不会有类似于ZooKeeper的选举leader的过程,客户端请求会自动切换到新的Eureka节点
  • 当宕机的服务器重新恢复后,Eureka会再次将其纳入到服务器集群管理之中;
    而对于Eureka来说,所有要做的无非是同步一些新的服务注册信息而已。再也不用担心有“掉队”的服务器恢复以后,会从Eureka服务器集群中剔除出去的风险了。
  • Eureka甚至被设计用来应付范围更广的网络分割故障,并实现“0”宕机维护需求。当网络分割故障发生时,每个Eureka节点,会持续的对外提供服务(注:ZooKeeper不会),接收新的服务注册同时将它们提供给下游的服务发现请求。这样一来,就可以实现在同一个子网中(same side of partition),新发布的服务仍然可以被发现与访问。
  • 正常配置下,Eureka内置了心跳服务,用于淘汰一些“濒死”的服务器
  • 如果在Eureka中注册的服务,它的“心跳”变得迟缓时,Eureka会将其整个剔除出管理范围(这点有点像ZooKeeper的做法)

这是个很好的功能,但是当网络分割故障发生时, 这也是非常危险的;因为,那些因为网络问题,而被剔除出去的服务器本身是很”健康“的,只是因为网络分割故障把Eureka集群分割成了独立的子网而不能互访而已。幸运的是,Netflix考虑到了这个缺陷。如果Eureka服务节点在短时间里丢失了大量的心跳连接(注:可能发生了网络故障),那么这个Eureka节点会进入”自我保护模式“,同时保留那些“心跳死亡“的服务注册信息不过期。此时,这个Eureka节点对于新的服务还能提供注册服务,对于”死亡“的仍然保留,以防还有客户端向其发起请求。当网络故障恢复后,这个Eureka节点会退出”自我保护模式“。所以Eureka的哲学是,同时保留”好数据“与”坏数据“总比丢掉任何”好数据“要更好,所以这种模式在实践中非常有效。

最后,Eureka还有客户端缓存功能(注:Eureka分为客户端程序与服务器端程序两个部分,客户端程序负责向外提供注册与发现服务接口)。所以即便Eureka集群中所有节点都失效,或者发生网络分割故障导致客户端不能访问任何一台Eureka服务器;Eureka服务的消费者仍然可以通过Eureka客户端缓存来获取现有的服务注册信息。甚至最极端的环境下,所有正常的Eureka节点都不对请求产生相应,也没有更好的服务器解决方案来解决这种问题时;得益于Eureka的客户端缓存技术,消费者服务仍然可以通过Eureka客户端查询与获取注册服务信息,这点很重要。

Eureka的构架保证了它能够成为Service发现服务。它相对与ZooKeeper来说剔除了Leader节点的选取或者事务日志机制,这样做有利于减少使用者维护的难度也保证了Eureka的在运行时的健壮性。而且Eureka就是为发现服务所设计的,它有独立的客户端程序库,同时提供心跳服务、服务健康监测、自动发布服务与自动刷新缓存的功能。但是,如果使用ZooKeeper你必须自己来实现这些功能。Eureka的所有库都是开源的,所有人都能看到与使用这些源代码,这比那些只有一两个人能看或者维护的客户端库要好。

维护Eureka服务器也非常的简单,比如,切换一个节点只需要在现有EIP下移除一个现有的节点然后添加一个新的就行。Eureka提供了一个web-based的图形化的运维界面,在这个界面中可以查看Eureka所管理的注册服务的运行状态信息:是否健康,运行日志等。Eureka甚至提供了Restful-API接口,方便第三方程序集成Eureka的功能。

结论


关于Service发现服务通过本文我们想说明两点:

  • 留意服务运行的硬件平台
  • 时刻关注你要解决的问题,然后决定 使用什么平台。
  • 云部署平台是充满不可靠性的,Eureka可以应对这些缺陷,同时Service发现服务必须同时具备高可靠性与高弹性

Eureke就是我们想要的!

上一篇