为什么要使用服务发现功能?

当我们写一些代码来调用基于REST API 或Thrift API的服务时,为了构建一个请求,我们需要知道服务实例的网络地址(IP和端口)。在基于物理硬件的传统应用中,服务实例的地址相对固定。比如,代码可以从配置文件中读取服务的网络地址,这些地址只会偶尔更新。
但是,在现代的基于云的微服务应用中,这却是一个需要解决的难题,如下图所示:

微服务架构中的服务发现(Service Discovery)插图

上图中,服务实例是基于动态分配的网络地址,而且服务会发生自动增减、故障和升级等动态的变化。所以,对于客户端程序来说,需要使用更精确的服务发现机制。

主要有两种服务发现模式:客户端发现和服务端发现。先来看一下客户端发现。

客户端发现模式

当使用客户端发现模式时,客户端负责判断可用的服务实例和请求的负载均衡。客户端会查询一个服务注册表,该注册表是可用服务实例的数据库。客户端使用负载均衡算法从可用的服务实例中选择一个,然后发起请求。


下图为这种模式的架构图:

微服务架构中的服务发现(Service Discovery)插图1

当服务实例启动时,会将自己的网络地址信息注册到服务注册表,当服务终止时它会被服务注册表移除。通常使用心跳机制定刷新服务实例的注册。


Netflix OSS提供了一个非常棒的客户端发现模式的实例。Netflix Eureka是一个服务注册表,它提供了基于REST API的服务实例注册和可用服务实例查询功能。Netflix Ribbon是一个IPC客户端,配合Eureka实现对服务实例请求的负载均衡。在文章的后面我们会深入学习一下Eureka。


客户端发现模式有一系列的优点和缺点。这种模式相对简单,除服务注册表外,没有其他变动部分。同时,由于客户端知道所有的可用服务实例,因此,它可以做出更明智的、基于特定应用负载均衡决策,比如使用一致性哈希算法。这种模式的一个重要缺点是它将客户端和服务注册表耦合在了一起。你必须为客户端使用的每种编程语言和框架实现客户端的服务发现逻辑。


了解了客户端发现之后,我们来看看服务器端发现。

服务器端发现模式


服务器端发现的另外一种方式就是服务器发现模式。下图中展示了该模式的结构:

微服务架构中的服务发现(Service Discovery)插图2

客户端通过负载均衡器向服务发起请求。负载均衡器查询服务注册表,并将每个请求路由到可用的服务实例。与客户端发现先比,服务实例是通过服务注册表进行注册和注销的。

AWS的ELB(Elastic Load Balancer)是服务器发现路由器的示例。ELB通常用于来自外网的流量的负载均衡,但是你也可以使用ELB来负载均衡私有云(VPV)内部的流量。客户端使用DNS名称,通过ELB发送请求(Http或TCP),ELB在已注册的弹性计算云(EC2)实例或EC2容器服务(ECS)的容器之间进行负载均衡。这种实现并没有单独的服务注册表,而是将EC2实例和ECS容器注册到ELB自身上。

Http服务器和负载均衡器(比如,Nginx plus和Nginx)也可以用作服务器发现的负载均衡器。比如,使用Consul模板动态配置Nginx反向代理。Consul模板是一个工具,可以从存储在Consul服务注册表中的配置数据中定时重新生成任意配置文件。每当文件改变时,它可以运行一个任意shell命令。在本例中,Consul模板生成一个nginx.conf文件,用于配置反向代理,然后执行命令告诉Nginx去重新加载配置。更复杂的实现可以使用Http API后DNS动态重新配置Nginx plus。

某些部署环境(例如Kubernetes和Marathon)会在集群中的每个主机上运行一个代理。这个代理扮演服务器端发现负载平衡器的角色。客户端为了向服务发出请求,会通过代理基于主机IP和服务端口进行路由。然后,代理透明地将请求准发到集群中某个可用的服务实例。

服务器端发现模式同样有一些优点和缺点。这种模式最大的优点是,服务发现的细节从客户端抽离出来了。客户端只用发送请求到负载均衡器即可。这样就无需为客户端服务使用的每种编程语言和框架实现服务发现逻辑。此外,如上所述,某些部署环境已经免费提供了该功能。当然,这种模式也有一些缺点,除非负载均衡器是由部署环境提供,它还需要你搭建和管理另外一个高可用系统组件。

服务注册表

服务注册表是服务发现的关键部分,它是一个包含服务实例网络地址信息的数据库。服务注册表需要具有高可用性和实时更新性。客户端可以缓存从注册表获得的服务实例网络地址信息。但信息最终会过时,客户端也将无法发现服务实例。因此,服务注册表需要由一组服务构成集群,集群之间通过复制协议维持一致性。

前面已经提到,Netflix Eureka是一个很好的服务注册表示例。它提供了基于REST API形式的服务实例注册和查询功能。一个服务实例可以通过POST请求将自己的网络地址进行注册。每隔30秒,通过PUT请求刷新它的注册信息。可以通过Http的DELETE请求或超时机制来删除实例的注册信息。客户端也可以使用Http的GET请求来检索注册的服务实例。

Netflix通过在每个Amazon EC2的可用区域中运行一台或多台Eureka服务器来实现高可用性。运行在EC2实例上的每个Eureka服务器都有一个弹性IP地址。DNS TEXT用于存储Eureka群集配置,该配置是可用区域到Eureka服务器网络位置列表的映射。当Eureka服务器启动时,查询DNS来检索Eureka群集配置,定位其他服务,并为自己分配一个未使用的弹性IP地址。

服务注册表的其他组件:

etcd:高可用性、分布式、一致性、键值存储,用于共享配置和服务发现。Kubernetes和Cloud Foundry使用的便是etcd。

Consul:用于发现和配置服务的工具。客户端可基于API注册和发现服务。Consul可以进行健康检查以确定服务可用性。

Apache Zookeeper:广泛用于分布式应用程序的高性能协调服务。 Apache Zookeeper最初是Hadoop的子项目,但现在是顶级项目。

另外,如前所述,某些系统(如Kubernetes,Marathon和AWS)没有明确的服务注册表。相反,服务注册表只是基础设置内置功能的一部分。

既然我们已经了解了服务注册表的概念,那么让我们看一下如何在服务注册表中注册服务实例。

服务注册选项

前面已经提到,服务实例必须通过注册表进行注册或注销,通常有几种不同方式来处理注册和注销。一种选择是服务实例进行自我注册,即自我注册模式。另外一种选择是基于其他系统组件来管理服务实例的注册,即第三方注册模式。先来看一下自我注册模式。

自我注册模式

当使用自我注册模式时,服务实例负责在服务注册表中进行自身的注册和注销。如果需要,服务实例还需要发送心跳请求以避免因超时而被注销。下图展示了这种模式的结构图:

微服务架构中的服务发现(Service Discovery)插图3

Netflix OSS Eureka客户端就是这种模式的一个很好的例子。Eureka客户端负责处理服务实例所有的注册和注销事项。在Spring Cloud项目中,实现了包括服务发现的各种模式,进而可以很轻松的实现自动注册服务实例到Eureka。你只需在Java配置类上使用@EnableEurekaClient注解即可。

自我注册模式有一些优点和缺点。优点是使用起来非常简单,不需要任何其他系统组件。然而,主要的缺点是服务实例与服务注册表紧密耦合,需要在每种编程语言和框架中实现注册代码。

另外一种方式可以让服务和注册表解耦的方式就是第三方注册模式。

第三方注册模式

当使用第三方注册模式时,服务实例不再负责将自己注册到服务注册表。相反,第三方组件作为服务注册商来处理注册。服务注册商通过轮询部署环境或订阅事件来跟踪实例的变化。当发现新的可用服务实例时,它会将服务实例注册到服务注册表中。同时,也会注销已经停止的服务实例。下图展示了这种模式的结构:

微服务架构中的服务发现(Service Discovery)插图4

开源项目Registrator便是一个示例,它可以基于Docker容器自动注册和注销服务实例。Registrator支持多种注册表,包括etcd和Consul。

NetflixOSS Prana项目是另外一个示例,它主要用于非JVM语言编写的服务,是与服务实例并行的Sidecar应用程序。Prana基于Netflix Eureka注册和注销服务实例。

服务注册商是部署环境的内置组件。由自动伸缩组创建的EC2实例可以自动向ELB注册。Kubernetes服务将自动注册并可供发现。

第三方注册模式的一个主要好处是服务与服务注册表分离。无需为开发人员使用的每种编程语言和框架实现服务注册逻辑。而是在专用服务内以集中方式处理服务实例注册。这种模式缺点是,除非将其内置到部署环境中,否则还需要额外搭建和管理一个高度可用的系统组件。

总结

在微服务应用程序中,服务实例运行状态会动态更改,实例会动态分配的网络地址。因此,为了使客户端可以正常请求服务,必须使用服务发现机制。

服务发现的关键是服务注册表,它是可用服务实例的数据库。服务注册表提供了管理和查询的API。服务实例使用管理API在服务注册表中注册或注销。系统组件使用查询API来发现可用的服务实例。

有两种主要的服务发现模式:客户端发现和服务端发现。在使用客户端服务发现的系统中,客户端查询服务注册表,选择可用实例,然后发出请求。 在使用服务器端发现的系统中,客户端通过路由器发出请求,该路由器查询服务注册表并将请求转发到可用实例。

服务实例在服务注册表中注册和注销的主要方法有两种。 一种选择是让服务实例在服务注册表(自我注册模式)中进行自身注册。 另一个选项是让某些其他系统组件代理服务(第三方注册模式)来处理注册和注销。

在某些部署环境中,需要使用服务注册表(例如Netflix Eureka,etcd或Apache Zookeeper)来建立自己的服务发现基础架构。 在其他部署环境中,内置了服务发现。例如,Kubernetes和Marathon处理服务实例的注册和注销。 它们在每台服务器上运行代理程序,用来充当服务器端发现路由器角色。

HTTP反向代理和负载均衡器(例如NGINX)也可以用作服务器端发现负载均衡器。 服务注册中心可以推送路由信息到NGINX,并调用正常的配置更新。 比如,你可以使用Consul模板。 NGINX Plus支持其他动态重新配置机制–它可以使用DNS从注册表中获取有关服务实例的信息,并且提供用于远程重新配置的API。



微服务架构中的服务发现(Service Discovery)插图5

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://choupangxia.com/2021/05/01/service-discovery/