谈到分布式,注册中心肯定是我们必不可少的一项,那么注册中心又千奇百怪,怎么为项目选择一个最佳的注册中心呢,那么就简单聊聊三大注册中心的好处以及弊端。
在聊注册中心前,我们得了解一下CAP(C:一致性 A:可用性 P:分批容错性)
eureka的优势
1:谈到eureka,首先你得知道它是AP的,不是我们LOL里的AP,而是他的可用性和容错性。为什么可用性比较高呢,比如在发生某个注册中心下的服务突然挂掉,Eureka不会有类似于ZooKeeper的选举leader的过程,那么他默认会轮询到下一个结点请求服务,当宕机的服务器重新恢复后,Eureka会再次将其纳入到服务器集群管理之中;而对于它来说,所有要做的无非是同步一些新的服务注册信息而已。所以,再也不用担心有“掉队”的服务器恢复以后,会从Eureka服务器集群中剔除出去的风险了。Eureka甚至被设计用来应付范围更广的网络分割故障,并实现“0”宕机维护需求。(多个zookeeper之间网络出现问题,造成出现多个leader,发生脑裂)当网络分割故障发生时,每个Eureka节点,会持续的对外提供服务(注:ZooKeeper不会):接收新的服务注册同时将它们提供给下游的服务发现请求。这样一来,就可以实现在同一个子网中(same side of partition),新发布的服务仍然可以被发现与访问。
2、正常配置下,Eureka内置了心跳服务,用于淘汰一些“濒死”的服务器;如果在Eureka中注册的服务,它的“心跳”变得迟缓时,Eureka会将其整个剔除出管理范围(这点有点像ZooKeeper的做法)。这是个很好的功能,但是当网络分割故障发生时,这也是非常危险的;因为,那些因为网络问题(注:心跳慢被剔除了)而被剔除出去的服务器本身是很”健康“的,只是因为网络分割故障把Eureka集群分割成了独立的子网而不能互访而已。
幸运的是,Netflix考虑到了这个缺陷。如果Eureka服务节点在短时间里丢失了大量的心跳连接(注:可能发生了网络故障),那么这个Eureka节点会进入”自我保护模式“,同时保留那些“心跳死亡“的服务注册信息不过期。此时,这个Eureka节点对于新的服务还能提供注册服务,对于”死亡“的仍然保留,以防还有客户端向其发起请求。当网络故障恢复后,这个Eureka节点会退出”自我保护模式“。所以Eureka的哲学是,同时保留”好数据“与”坏数据“总比丢掉任何”好数据“要更好,所以这种模式在实践中非常有效。
3、Eureka还有客户端缓存功能(注:Eureka分为客户端程序与服务器端程序两个部分,客户端程序负责向外提供注册与发现服务接口)。所以即便Eureka集群中所有节点都失效,或者发生网络分割故障导致客户端不能访问任何一台Eureka服务器;Eureka服务的消费者仍然可以通过Eureka客户端缓存来获取现有的服务注册信息。甚至最极端的环境下,所有正常的Eureka节点都不对请求产生相应,也没有更好的服务器解决方案来解决这种问题
时;得益于Eureka的客户端缓存技术,消费者服务仍然可以通过Eureka客户端查询与获取注册服务信息,这点很重要。
4、Eureka的构架保证了它能够成为Service发现服务。它相对与ZooKeeper来说剔除了Leader节点的选取或者事务日志机制,这样做有利于减少使用者维护的难度也保证了Eureka的在运行时的健壮性。而且Eureka就是为发现服务所设计的,它有独立的客户端程序库,同时提供心跳服务、服务健康监测、自动发布服务与自动刷新缓存的功能。但是,如果使用ZooKeeper你必须自己来实现这些功能。Eureka的所有库都是开源的,所有人都能看到与使用这些源代码,这比那些只有一两个人能看或者维护的客户端库要好。
5、维护Eureka服务器也非常的简单,比如,切换一个节点只需要在现有EIP下移除一个现有的节点然后添加一个新的就行。Eureka提供了一个web-based的图形化的运维界面,在这个界面中可以查看Eureka所管理的注册服务的运行状态信息:是否健康,运行日志等。Eureka甚至提供了Restful-API接口,方便第三方程序集成Eureka的功能。
zookeeper的优势
zookeeper的特性即是它的CP,主要使用场景远不是满足最初设计时对一致性调解的需求,这么受欢迎是因为其灵活的特性设计,只要简单组合就能满足很多种需求,遗憾的是zookeeper原本不是为高可用性设计的。
1.顺序一致性:从一个客户端发起的事务请求,最终都会严格按照其发起顺序被应用到Zookeeper中;对于来自客户端的每个更新请求,Zookeeper都会分配一个全局唯一的递增ID(zxid),这个ID反映了所有事务请求的先后顺序。
2.原子性:所有事务请求的处理结果在整个集群中所有机器上都是一致的
3.最终一致性:所有客户端看到的服务端数据模型都是一致的;
4.可靠性:一旦服务端成功应用了一个事务,则其引起的改变会一直保留,直到被另外一个事务所更改,如果消息被到一台服务器接受,那么它将被所有的服务器接受。
5.实时性:一旦一个事务被成功应用后,Zookeeper可以保证客户端立即可以读取到这个事务变更后的最新状态的数据。
6.等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。
zookeeper的缺点
1:zookeeper不是为高可用性设计的
2:由于要跨机房容灾,很多系统实际上是需要跨机房部署的。出于性价比的考虑我们通常会让多个机房同时工作,而不会搭建N倍的冗余。也就是说单个机房肯定撑不住全流量(你能设想谷歌在全球只剩下一个机房在干活吗)。由于zookeeper集群只能有一个master,因此一旦机房之间连接出现故障,zookeeper master就只能照顾一个机房,其他机房运行的业务模块由于没有master都只能停掉。于是所有流量集中到有master的那个机房,于是系统crash。
即使是在同一个机房里面,由于网段的不同,在调整机房交换机的时候偶尔也会发生网段隔离的情况。实际上机房每个月基本上都会发生短暂的网络隔离之类的子网段调整。在那个时刻zookeeper将处于不可用状态。如果整个业务系统基于zookeeper(比如要求每个业务请求都先去zookeeper获取业务系统的master地址),则系统的可用性将非常脆弱。
3:由于zookeeper对于网络隔离的极度敏感,导致zookeeper对于网络的任何风吹草动都会做出激烈反应。这使得zookeeper的‘不可用’时间比较多,我们不能让zookeeper的‘不可用’,变成系统的不可用。
zookeeper的选举过程速度很慢
这是一个很难从理论分析上看到的弱点,但是你一旦遇到就会痛不欲生。
前面我们已经说过,网络实际上常常是会出现隔离等不完整状态的,而zookeeper对那种情况非常敏感。一旦出现网络隔离,zookeeper就要发起选举流程。
zookeeper的选举流程通常耗时30到120秒,期间zookeeper由于没有master,都是不可用的。
对于网络里面偶尔出现的,比如半秒一秒的网络隔离,zookeeper会由于选举过程,而把不可用时间放大几十倍。
4:zookeeper的性能是有限的
典型的zookeeper的tps大概是一万多,无法覆盖系统内部每天动辄几十亿次的调用。因此每次请求都去zookeeper获取业务系统master信息是不可能的。
因此zookeeper的client必须自己缓存业务系统的master地址。
因此zookeeper提供的‘强一致性’实际上是不可用的。如果我们需要强一致性,还需要其他机制来进行保障:比如用自动化脚本把业务系统的old master给kill掉,但是那会有很多陷阱(这里先不展开这个议题,读者可以自己想想会有哪些陷阱)。
zookeeper无法进行有效的权限控制
zookeeper的权限控制非常薄弱
在大型的复杂系统里面,使用zookeeper必须自己再额外的开发一套权限控制系统,通过那套权限控制系统再访问zookeeper
额外的权限控制系统不但增加了系统复杂性和维护成本,而且降低了系统的总体性能
即使有了zookeeper也很难避免业务系统的数据不一致
5:前面已经讨论过了,由于zookeeper的性能限制,我们无法让每次系统内部调用都走zookeeper,因此总有某些时刻,业务系统会存在两个master(业务系统client那边缓存的业务系统master信息是定时从zookeeper更新的,因此会有更新不同步的问题)。
如果要在业务系统client的master信息不一直的情况下,仍要保持系统的数据一致性,唯一的方法是“先kill掉老master,再在zookeeper上更新master信息”。但是在是否要kill current master这个问题上,程序是无法完全自动决定的(因为网络隔离的时候zookeeper已经不可用了,自动脚本没有全局信息,不管怎么做都可能是错的,什么都不做也可能是错的。当网络故障的时候,只有运维人员才有全局信息,程序是无法接电话得知其他机房的情况的)。因此系统无法自动的保障数据一致性,必须要人工介入。而人工介入的典型时间是半个小时以上,我们不能让系统这么长时间不可用。因此我们必须在某个方向上进行妥协,最常见的妥协方式是放弃‘强一致性’,而接受‘最终一致性’。
如果我们需要人工介入才能保证‘可靠的强一致性’,那么zookeeper的价值就大打折扣。
Consul的优点
简单介绍一下Consul是HashiCorp公司推出的开源工具,Consul由Go语言开发,部署起来非常容易,只需要极少的可执行程序和配置文件,具有绿色、轻量级的特点。Consul是分布式的、高可用的、 可横向扩展的用于实现分布式系统的服务发现与配置。
1:Consul在一个单一的数据中心内部使用服务节点。在每个数据中心中,为了Consule能够运行,并且保持强一致性,Consul服务端需要仲裁。然而,Consul原生支持多数据中心,就像一个丰富gossip系统连接服务器节点和客户端一样。
:2:当提供K/V存储的时候,这些系统具有大致相同的语义,读取是强一致性的,并且在面对网络分区的时候,为了保持一致性,读取的可用性是可以牺牲的。然而,当系统应用于复杂情况时,这种差异会变得更加明显。
:3:这些系统提供的语义对开发人员构建服务发现系统很有吸引力,但更重要的是,强调开发人员要构建这些特性。ZooKeeper只提供一个原始的K/V值存储,并要求开发人员构建他们自己的系统来提供服务发现功能。相反的是,Consul提供了一个坚固的框架,这不仅仅是为了提供服务发现功能,也是为了减少推测工作和开发工作量。客户端只需简单地完成服务注册工作,然后使用一个DNS接口或者HTTP接口就可以执行工作了,而其他系统则需要你定制自己的解决方案。
4:一个令人信服的服务发现框架必须包含健康检测功能,并且考虑失败的可能性。要是节点失败或者服务故障了,即使开发人员知道节点A提供Foo服务也是没用的。Navie系统利用的是心跳、周期性更新和TTLs,这些系统不仅需要工作量与节点数量成线性关系,并且对服务器的固定数量提出了要求。此外,故障检测窗口的存活时间至少要和TTL一样长
5:Consul使用一个完全不同的架构进行健康检测。Consul客户端可以运行在集群中的每一个节点上,而不是拥有服务器节点,这些Consul客户端属于一个gossip pool,gossip pool提供了一些功能,包括分布式健康检测。gossip协议提供了一个高效的故障检测工具,这个故障检测工具可以应用到任意规模的集群,而不仅仅是作用于特定的服务器组。同时,这个故障检测工具也支持在本地进行多种健康检测。与此相反,ZooKeeper的临时节点只是一个非常原始的活跃度检测。因为有了Consul,客户端可以检测web服务器是否正在返回200状态码,内存利用率是否达到临界点,是否有足够的数据存储盘等。此外,ZooKeeper会暴露系统的复杂性给客户端,为了避免ZooKeeper出现的这种情况,Consul只提供一个简单HTTP接口。
6:Consul为服务发现、健康检测、K/V存储和多数据中心提供了一流的支持。为了支持任意存储,而不仅仅是简单的K/V存储,其他系统都要求工具和lib库要率先建立。然而,通过使用客户端节点,Consul提供了一个简单的API,这个API的开发只需要瘦客户端就可以了, 而且,通过使用配置文件和DNS接口,开发人员可以建立完整的服务发现解决方案,最终,达到避免开发API的目的。
如需交流请加qq群:103129216