这是我们系列中关于使用微服务构建应用程序的第四篇文章。在本文中,我们将探讨与服务发现密切相关的问题。
让我们假设正在编写一些代码来调用具有REST API或Thrift API的服务。为了发出请求,代码需要知道服务实例的网络位置(IP地址和端口)。
服务实例具有动态分配的网络位置。此外,由于自动扩展,故障和升级,服务实例集会动态更改。因此,客户端代码需要使用更复杂的服务发现机制。有两种主要的服务发现模式:
我们先来看看客户端发现。
使用客户端发现时,客户端负责确定可用服务实例的网络位置以及跨这些实例的负载均衡请求。客户端查询服务注册表,该服务注册表是可用服务实例的数据库。然后,客户端使用负均衡算法选择一个可用的服务实例并发出请求。下图显示了此模式的结构。
服务实例的网络位置在启动时向服务注册表注册。实例终止时,它将从服务注册表中删除。通常使用心跳机制定期刷新服务实例的注册。
Netflix OSS提供了客户端发现模式的一个很好的例子。Netflix Eureka是一个服务注册表。它提供了一个REST API,用于管理服务实例注册和查询可用实例。Netflix Ribbon是一个IPC客户端,与Eureka一起在可用服务实例之间加载均衡请求。我们将在本文后面更深入地讨论Eureka。
客户端发现模式具有各种优点:这种模式相对简单,除了服务注册表之外,没有其他移动部件。所以,当客户端知道了可用的服务实例后,就可以进行智能的、基于特定应用程序的负载均衡决策算法(例如,使用一致性哈希算法)来访问服务。
这种模式的一个重要缺点是:它将客户端与服务注册表耦合在一起,必须为服务使用的每种编程语言和框架实现客户端服务发现逻辑。
现在我们已经了解了客户端发现,让我们来看看服务器端发现。
服务发现的另一种方法是服务器端发现模式。下图显示了此模式的结构。
客户端通过负载均衡器向服务发出请求。负载均衡器查询服务注册表并将每个请求路由到可用的服务实例。与客户端发现一样,服务实例在服务注册表中注册或注销。
AWS Elastic Load Balancer(ELB)是服务器端发现路由器的示例。ELB通常用于对来自Internet的外部流量进行负载均衡。但是,也可以使用ELB来均衡虚拟私有云(VPC)内部的流量。客户端使用其DNS名称通过ELB发出请求(HTTP或TCP)。 ELB负载均衡一组已注册的弹性计算云(EC2)实例或EC2容器服务(ECS)容器之间的流量。不需要单独的服务注册表。相反,EC2实例和ECS容器已在ELB中注册完成。
HTTP服务器和负载均衡器(如NGINX Plus和NGINX)也可用作服务器端发现负载均衡器。例如,这篇博文描述了使用Consul Template动态重新配置NGINX反向代理。
Consul Template是一种工具,可定期从存储在Consul服务注册表中的配置数据中重新生成配置文件。每当文件更改时,它都会运行shell命令。
在博客文章描述的示例中,Consul Template生成一个nginx.conf
文件,该文件用于配置反向代理,然后运行一个命令使NGINX重新加载配置。更复杂的实现可以使用HTTP API或DNS动态重新配置NGINX Plus。
某些部署环境(如Kubernetes和Marathon)在群集中的每个主机上运行一个代理服务。代理服务作为服务器端发现模式负载均衡器使用。为了向服务发出请求,客户端使用主机的IP地址和端口通过代理服务路由请求。然后,代理将请求透明地转发到在群集中某处运行的可用服务实例。
服务器端发现模式的优势和不足:
服务注册表是服务发现的关键部分,它是一个包含服务实例网络位置的数据库。服务注册表需要具有高可用性和数据最新性。
客户端可以缓存从服务注册表获取的网络位置,但是,该信息最终会过时而导致客户端无法发现服务实例。服务注册表由一组服务组成,这些服务器通过复制协议来保持数据的一致性。
如前所述,Netflix Eureka是服务注册表的一个很好的例子。它提供了一个REST API,用于注册和查询服务实例。
Netflix通过在每个Amazon EC2可用区域中运行一个或多个Eureka服务器来实现高可用性。每个Eureka服务器都在具有弹性IP地址的EC2实例上运行。DNS TEXT记录用于存储Eureka群集配置,该配置是从可用区域到Eureka服务器的网络位置列表的映射。当Eureka服务器启动时,它会查询DNS以检索Eureka群集配置,找到其对等端,并为自己分配未使用的弹性IP地址。
Eureka客户端通过查询DNS来发现Eureka服务器的网络位置。客户端更希望在同一可用区中使用Eureka服务器,但是,如果在同一个可用区内没有可访问的Eureka服务器,那么客户端会在另一个可用区中访问Eureka服务器。
服务注册表的其他示例包括:
此外,如前所述,某些系统(如Kubernetes,Marathon和AWS)没有明确的服务注册表。服务注册表只是基础结构的内置部分。现在我们已经了解了服务注册表的概念,让我们看看如何在服务注册表中注册服务实例。
如前所述,服务实例必须在服务注册表中注册和注销。有几种不同的方式来处理注册和注销。
使用自注册模式时,服务实例负责向服务注册表注册和注销自身,如果需要,服务实例会发送心跳请求以防止其注册过期。下图显示自注册模式的结构。
这种方法的一个很好的例子是Netflix OSS Eureka客户端,它会处理服务实例注册和注销的所有方面。Spring Cloud项目实现了各种模式,包括服务发现,可以轻松地使用Eureka自动注册服务实例。只需使用@EnableEurekaClient
批注对Java Configuration类进行批注。
自注册模式的优缺点:
将服务与服务注册表分离的替代方法是第三方注册模式。
使用第三方注册模式时,服务实例不负责向服务注册表注册自己。相反,另一个称为服务注册器的系统组件处理注册。服务注册器通过轮询部署环境或订阅事件来跟踪运行实例集的更改。当它发现有新的服务实例运行时,会将新的服务实例注册到服务注册表中,同时,服务注册器还负责注销已终止运行的服务实例。下图显示了此模式的结构。
服务注册器的例子:
服务注册器是部署环境的内置组件。由Autoscaling Group创建的EC2实例可以自动注册到ELB。Kubernetes的services会自动注册并被发现。
第三方注册模式的优缺点:
在微服务应用程序中,正在运行的服务实例集合会动态更改,每个服务实例都会动态分配网络位置。因此,客户端要向服务实例发出请求,就必须使用服务发现机制。
服务发现的关键部分是服务注册表,它是可用服务实例的数据库,提供管理API和查询API。
有两种主要的服务发现模式:
服务实例在服务注册表中注册和注销有两种主要方式:
在某些部署环境中,需要使用服务注册表(如Netflix Eureka,etcd或Apache Zookeeper)作为服务发现基础组件。
在其他部署环境中,内置了服务发现。例如,Kubernetes和Marathon自带处理服务实例注册和注销的功能,它们在每台主机上运行一个代理服务器作为服务器端发现模式中的负载均衡器。
HTTP反向代理和负载均衡器(如NGINX)也可用作服务器端发现模式中的负载均衡器。服务注册表可以将路由信息推送到NGINX并调用正常的配置更新;例如,可以使用Consul Template。NGINX Plus支持其他动态重新配置机制,它可以使用DNS从注册表中提取有关服务实例的信息,并提供用于远程重新配置的API。在未来的博客文章中,我们将继续深入研究微服务的其他方面。