假如你正在设计和开发一个分布式服务系统,系统中存在一批能够独立运行的服务,而在部署上也采用了集群模式以防止出现单点故障。显然,对于一个完整的业务系统而言,这些服务之间需要相互调用并形成复杂的访问链路,一种可能的系统运行时状态如下所示:
如果你所开发的系统一直处于上图中的稳定状态,也就是说没有新的服务需要添加,正在运行的服务实例也不会出现宕机等故障,那么一切看上去都没有什么问题。但显然,这是不现实的。一方面,业务的快速发展势必要求服务本身不断迭代,所以系统中服务的拆分方式和数量需要随之变化和演进。另一方面,任何服务也无法保证不会出现异常情况或发布需求,导致服务需要重新启动。
以上场景就揭示了构建分布式服务系统中所面临的一些挑战,主要包括两个方面,即:
- 服务实例的数量如何进行管理?
- 服务实例的状态如何进行管理?
正如上图中所展示的,假设一个系统中存在几十个甚至上百个服务时,开发人员可能甚至都无法明确系统中到底有哪些服务正在运行。另一方面,因为很难同时确保所有服务都不出现问题,也很难保证当前的服务部署方式不做调整和优化,因此各个服务自身对外暴露的访问地址也具有动态性。由于自动扩容、服务重启等因素,服务实例的运行时状态会经常变化,如下图所示:
为了更好的描述服务的运行时状态,我们可以对每个服务实例信息进行抽象,提取如下所示的状态信息:
这样,上图中所涉及的服务实例变化信息就可以通过更加统一而形象的方式表达出来,如下所示:
既然服务数量的增加以及服务实例的变化都不可避免,那么有什么好的办法能够做到对这些服务实例进行有效的管理呢?这实际上就是一个服务治理的问题,服务治理的需求来自于服务的数量,也来自于服务实例的动态性。为了实现高效的服务治理,通常都需要构建一个独立的媒介来管理服务的实例,这个媒介就是我们今天要介绍的注册中心。
注册中心是服务实例信息的存储仓库,也是服务提供者和服务消费者进行交互的桥梁,提供了服务注册和服务发现这两大核心功能,如下所示:
上图中,访问注册中心的客户端程序一般会嵌入在服务提供者和服务消费者内部。在服务启动时,服务提供者通过内部的注册中心客户端程序自动将自身注册到注册中心,这就是服务注册过程。
而服务消费者则通过对自己感兴趣的服务进行订阅,从而从注册中心中获取那些已经注册的服务实例信息,这就是服务发现过程。同时,为了提高服务发现过程的效率和容错性,服务消费者可以配备缓存机制以保存已经获取的服务实例信息。更重要的是当服务注册中心不可用时,服务消费者可以利用本地缓存路由实现对现有服务的可靠调用。
讲完这里,我们实际上已经了解了服务治理的主体内容。通过获取注册中心中的服务实例信息,我们可以掌握系统中服务的数量以及当前的运行时状态。但还有一个问题需要解决,即一旦服务的运行时状态发生了变更,我们如何有效获取这些变更信息呢?这就需要在注册中心中进一步引入变更通知机制,如下图所示:
变更通知机制是实现注册中心的一大难点。从架构设计上讲,状态变更管理可以采用注册中心本身具有的发布-订阅模式,这就诞生了一种服务监听机制。服务监听机制确保服务消费者能够实时监控服务更新状态,是一种被动接收变更通知的实现方案,通常采用监听器以及回调机制,如下图所示。
上图中,服务消费者可以对这些具体的服务实例节点添加监听器,当这些节点发生变化时(例如图中服务B的第一个实例变得不可用、服务C的第一个实例地址变更、或者是服务D新增了一个实例3),注册中心就能触发监听器中的回调函数确保更新通知到每一个服务消费者。显然,使用监听和通知机制具备实时的数据同步效果。
另外一种确保状态信息同步的方式是采用轮询机制。轮询机制是一种主动拉取策略,即服务的消费者定期调用注册中心提供的服务获取接口获取最新的服务列表并更新本地缓存,如下图所示:
轮询机制在实现上就是一个定时器,需要考虑的问题就是轮询的频率。为了确保数据同步的时效性,轮询频率不能太短。但考虑到轮询对注册中心的性能影响,也不能过于频繁的进行定时操作。一般而言,轮询频率控制在几十秒到几分钟之间是一种比较好的选择。
讨论完注册中心中服务实例存储和管理的核心功能之后,我们再来分析注册中心自身应该具备的高可用性,也就意味着注册中心本身需要构建对等集群。所谓对等集群,是指集群中所有的服务器都提供同样的数据,所以在对等集群中,作为注册中心客户端的各个服务只需要连接到一台注册中心服务器完成服务注册和发现即可,任何一台服务器宕机都不影响客户端正常使用,如下所示:
讲完注册中心的基本模型,我们再来分析目前市面上存在的一些代表性实现工具,常见的包括的Consul 、Zookeeper、Eureka、Nacos。其中Consul来自于HashiCorp公司,是一款用来实现分布式环境下服务发现与配置的开源工具。而Zookeeper是Apache顶级项目,作为分布式协调领域的代表性框架被广泛用于注册中心、配置中心、分布式锁等的构建场景。Netflix的Eureka则采用了一套完全不同的实现方案,被集成到微服务开发框架Spring Cloud中。而Nacos则由阿里巴巴开发,其核心定位是一个更易于帮助构建云原生应用的动态服务发现、配置和服务管理平台。
上述工具各有特色,都实现了注册中心的高可用性、服务实例存储以及同步功能,并提供了一组方便集成的客户端组件。这里以Zookeeper和Eureka为例来展开讨论,它们分别是Dubbo和Spring Cloud这两款微服务开发框架中的默认注册中心实现方案。
Zookeeper是“服务监听机制”实现策略的典型代表性工具。Zookeeper本质上是一个树形结构,可以在树上创建临时节点,并对节点添加监听器。临时节点的客户端与该节点建立长链接,并关注节点的状态,一旦发生变化则通过监听器回传消息到客户端,然后客户端的回调函数就会得到调用,如下图所示:
而对于Netflix Eureka而言,采用的就是典型的“轮询机制”来实现服务实例状态的同步,如下所示:
在Eureka中,Eureka的客户端会每隔30秒发送一次心跳来进行服务续约。通过续约来告知Eureka服务器该客户端仍然存在。在默认的情况下,当Eureka客户端连续90秒没有向服务器发送服务续约心跳,Eureka服务器就会将该服务实例从服务注册列表中删除,即剔除该服务。通过这种方式确保Eureka服务器中服务实例信息的正确性。而客户端则通过轮询机制获取这些服务实例信息,默认的轮询频率是30秒。
最后,作为总结,你只需要记住注册中心就是这样一种服务治理工具:管理系统中所有服务实例的运行时状态,并能够把这些状态的变化同步到各个服务中。在分布式系统中,你可以通过引入注册中心轻松实现对大规模服务的高效治理。