谈一谈我所理解的微服务中的注册中心( 二 )


注册中心的问题在微服务中,注册中心作为一个存储所有服务中心的地方必然不可能是单机的 。因为如果注册中心无法使用,那么服务提供者就无法对外暴露自己的服务,消费者也无法获取自己需要调用的服务的具体地址,整个应用将会崩溃 。首先需要保证的就是注册中心的高可用 。
一致性和可用性的抉择说到高可用,CAP理论是绕不过去的,CAP简单来说就是我们需要在服务的可用性和服务间的一致性进行一个抉择 。是牺牲可用性来保证强一致性还是保证可用性牺牲强一致性呢?
CP类型注册中心Zookeeper是一个典型的CP类型的高可用组件 。zookeeper实现来paxos算法,zookeeper集群有一个节点作为leader,如果leader节点挂了zk会通过ZAB协议来进行leader选举,但是在选举的过程中zk是不能对外提供服务的 。
而且当因为网络分区导致zk集群出现脑裂问题时,由于ZAB协议需要半数以上的节点参与,所以可能会有部分或者全部的zk节点无法对外提供服务 。但是zk可以保证所有可用节点都数据都是一致,即使节点崩溃,在恢复后也会和其他节点保持一致,这就是zk牺牲了可用性而保证的强一致性 。
AP类型注册中心而类似与eureka的注册中心则是AP类型的 。eureka实现高可用是通过启动多个eureka server服务,每一个eureka server即是提供者也是消费者,每个eureka server将自己作为服务注册给其他的eureka server,这样每个eureka server都是其他server 的replica 。eureka这种模式中每个节点都是平等的,不存在leader、flower 。
当集群中某一个server节点宕机,请求可以直接转移到其他节点 。即使发生了网络分区,尽管不同分区之间无法进行通信,但是在每一个分区内的节点是通信并且可以正常对外提供服务,这样就保证了在server节点宕机的情况下的注册中心的可用性 。
而问题在于由于不同分区无法通信,就导致可能一部分节点的数据和另一部分有差别,这就是牺牲了强一致性来换取系统可用性 。
注册中心的选择首先我们应该明确的是其实注册中心的存在与否应该是不能直接影响服务的调用的 。服务之间的调用时通过http或者rpc等协议直接调用的,注册中心只是提供一个调用地址 。服务是否可以正常使用不能直接靠注册中心节点信息来决定 。
即使注册中心出现问题,只要服务本身没有宕机,理论上来说还是应该正常对外提供服务 。所以很明显对于我们来说AP类型的注册中心是要优于CP类型的注册中心的 。
当然在实际实现过程中很多框架都会尽可能的去修补这些问题,比如eureka会定时的同步各个节点的数据,尽可能的做到数据一致性 。dubbo的zk注册中心实现过程中也会本地缓存服务信息并不是完全通过zk信息来决定是否剔除服务等 。所以实际在选择时还是需要根据自身实际以及是否还有其他需求来确定 。
混合语言开发在微服务中,每一个服务都是一个独立的整体 。各个服务之间并不向单体应用中的不同模块大都要求是同一语言实现 。现如今各大公司开发语言是多样化的,不同的业务线开发的语言可能都不尽相同,混合语言是我们必须要面对的一个问题,所以出现了多语言之间服务调用的问题 。
对于像eureka这样的应用内的注册中心 。由于服务的注册与发现都是依赖于SDK,所以如果要使用eureak那需要对应的语言实现的SDK,目前有不少语言都有提供如Python、node.js 。目前springclould的Sidecar组件也可以做到将其他语言纳入到springclould体系中来 。
对于应用外的注册中心,一般会由这些中间件提供客户端操作实现 。然后由语言本身来实现服务注册和治理等功能 。目前很多语言实际上也有相关的开源工程 。
节点存活判断前面已经说过了保证CP类型的强一致性注册中心以及AP类型保证可用性的注册中心 。而对于一个注册中心来说最重要的就是管理其中的节点信息 。服务启动的时候需要将服务信息记录,当服务出现问题的时候需要将服务摘除 。
但是问题在于如何实现这个摘除的操作,目前注册中心的实现都是通过心跳检测来判断服务的健康状态 。正常情况下如果服务宕机了,心跳检测无法和服务通信判断该节点无法正常服务,将服务从注册中心摘除,这个过程是没有问题的 。但是如果发生网络问题,比如网络抖动或者网络分区,这时候心跳检测肯定是无法正常通信的,但如果就因此直接将服务摘除那肯定是有问题的,因为节点实际上是能够正常提供服务的 。所以需要有机制来确保这个过程不会粗暴的将节点从注册中心摘除 。


推荐阅读