编者:本系列共7篇文章,现已全部发布:
还可以下载电子书,其中收录了全套文章以及使用NGINX Plus实施微服务的相关信息:微服务:从设计到部署。此外,请查看新的微服务解决方案页面。
本文是微服务应用构建系列的第4篇文章。第1篇文章介绍了微服务架构模式,并探讨了使用微服务的优缺点。本系列的第2<.htmla>篇和第3篇文章介绍了微服务架构中通信的不同方面。本文将探讨与此密切相关的服务发现问题。
为什么要使用服务发现?
假设您正在编写一段代码,用于调用提供REST API或Thrift API的服务。要发出请求,代码需要知道服务实例的网络位置(IP地址和端口)。在物理硬件上运行的传统应用中,服务实例的网络位置相对固定。例如,代码可以从偶尔更新的配置文件中读取网络位置。
然而,在现代云端微服务应用中,这个问题要难解决得多,如下图所示。

服务实例的网络位置是动态分配的。此外,服务实例集合也会因自动扩缩容、故障和升级而动态变化。因此,客户端代码需要使用更完善的服务发现机制。
服务发现主要有两种模式:客户端发现和服务端发现。我们先来了解客户端发现。
客户端服务发现模式
使用客户端发现时,客户端负责确定可用服务实例的网络位置,并在这些实例之间对请求进行负载均衡。客户端会查询服务注册表,即存储可用服务实例的数据库。然后,客户端使用负载均衡算法选择一个可用的服务实例并发出请求。
下图显示了此模式的结构。

服务实例启动时,其网络位置会注册到服务注册表中。实例终止时,该网络位置会从服务注册表中移除。通常会使用心跳机制定期刷新服务实例的注册信息。
Netflix OSS是客户端发现模式的典型示例。Netflix Eureka是一个服务注册中心。它提供REST API,用于管理服务实例注册和查询可用实例。Netflix Ribbon是一个IPC客户端,可与Eureka配合使用,在可用服务实例之间对请求进行负载均衡。本文稍后将深入介绍Eureka。
客户端发现模式有多种优点和缺点。该模式相对简单,除服务注册表外,不涉及其他组件。此外,由于客户端知道有哪些服务实例可用,因此可以根据具体应用智能地做出负载均衡决策,例如使用一致性哈希。该模式的一个显著缺点是客户端与服务注册表相耦合。必须针对服务客户端使用的每种编程语言和框架实现客户端服务发现逻辑。
了解了客户端服务发现后,接下来看看服务端服务发现。
服务端发现模式
服务发现的另一种方法是服务器端发现模式。下图展示了该模式的结构。

客户端通过负载均衡器向服务发出请求。负载均衡器查询服务注册中心,并将每个请求路由到可用的服务实例。与客户端服务发现一样,服务实例会在服务注册中心注册和注销。
The AWS Elastic Load Balancer(ELB)是服务器端服务发现路由器的一个示例。ELB通常用于对来自互联网的外部流量进行负载均衡。不过,也可以使用ELB对虚拟私有云(VPC)内部的流量进行负载均衡。客户端使用ELB的DNS名称,通过ELB发出请求(HTTP或TCP)。ELB在一组已注册的Elastic Compute Cloud(EC2)实例或EC2 Container Service(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地址和分配给服务的端口,通过代理路由请求。然后,代理会将请求透明地转发到集群中某处运行的可用服务实例。
服务端发现模式有多项优点,也存在一些缺点。该模式的一大优点是,发现过程的具体细节对客户端透明。客户端只需向负载均衡器发出请求,因此无需针对服务客户端使用的每种编程语言和框架分别实现发现逻辑。此外,如上所述,某些部署环境会免费提供此功能。不过,该模式也有一些缺点。除非部署环境提供负载均衡器,否则还需要设置和管理另一个高可用的系统组件。
服务注册表
The 服务注册表是服务发现的关键组成部分。它是一个包含服务实例网络位置的数据库。服务注册表需要具备高可用性,并保持最新状态。客户端可以缓存从服务注册表获取的网络位置。但是,这些信息最终会过时,导致客户端无法发现服务实例。因此,服务注册表由一组服务器集群构成,这些服务器使用复制协议来保持一致性。
如前所述,Netflix Eureka是服务注册表的一个典型示例。它提供REST API,用于注册和查询服务实例。服务实例使用POST请求注册其网络位置。服务实例必须每30秒使用PUT请求刷新注册信息。可以使用HTTP DELETE请求移除注册信息,也可以在实例注册超时后自动移除。客户端可以使用HTTP GET请求检索已注册的服务实例。
Netflix实现高可用性的方式是在每个Amazon EC2可用区中运行一台或多台Eureka服务器。每台Eureka服务器都在一个具有弹性IP地址的EC2实例上运行。DNS TEXT记录用于存储Eureka集群配置,该配置将各可用区映射到Eureka服务器网络位置列表。Eureka服务器启动时,会查询DNS以检索Eureka集群配置、查找对等服务器,并为自身分配一个未使用的弹性IP地址。
Eureka客户端(包括服务和服务客户端)通过查询DNS来发现Eureka服务器的网络位置。客户端优先使用同一可用区中的Eureka服务器。如果该可用区中没有可用的Eureka服务器,客户端则使用其他可用区中的Eureka服务器。
其他服务注册中心示例包括:
- etcd,一种高可用的分布式一致性键值存储,用于共享配置和服务发现。使用etcd的两个知名项目是Kubernetes和Cloud Foundry。
- consul:一种用于发现和配置服务的工具。它提供API,允许客户端注册和发现服务。Consul可以执行运行状况检查,以确定服务是否可用。
- Apache Zookeeper,一种广泛使用的高性能分布式应用协调服务。Apache Zookeeper最初是Hadoop的子项目,现已成为顶级项目。
此外,如前所述,Kubernetes、Marathon和AWS等一些系统没有显式的服务注册表。服务注册表只是基础设施的内置组成部分。
了解服务注册表的概念后,接下来看看如何将服务实例注册到服务注册表中。
服务注册选项
如前所述,服务实例必须在服务注册表中注册,并从中注销。注册和注销可通过几种不同的方式处理。其中一种方式是由服务实例自行注册,即“自注册模式”。另一种方式是由其他系统组件管理服务实例的注册,即“第三方注册模式”。下面先来了解自注册模式。
自注册模式
使用自注册模式时,服务实例负责在服务注册表中自行注册和注销。此外,如有需要,服务实例会发送心跳请求,以防注册过期。下图展示了此模式的结构。

这种方法的一个典型示例是Netflix OSS Eureka客户端。Eureka客户端负责处理服务实例注册和注销的各个环节。Spring Cloud项目实现了包括服务发现在内的多种模式,可轻松将服务实例自动注册到Eureka。只需为Java配置类添加@EnableEurekaClient注解即可。
自注册模式有多种优点和缺点。其优点之一是相对简单,不需要任何其他系统组件。但它也有一个主要缺点,即服务实例与服务注册中心紧密耦合。必须针对服务所使用的每种编程语言和框架实现注册代码。
另一种方法是第三方注册模式,它将服务与服务注册表解耦。
第三方注册模式
使用第三方注册模式时,服务实例无需自行向服务注册表注册。注册工作由另一个称为服务注册器的系统组件负责。服务注册器通过轮询部署环境或订阅事件来跟踪运行中实例集合的变化。发现新可用的服务实例时,服务注册器会将该实例注册到服务注册表中。服务注册器还会注销已终止的服务实例。下图展示了此模式的结构。

开源项目Registrator就是一种服务注册器。它会自动注册和注销以Docker容器形式部署的服务实例。Registrator支持多种服务注册中心,包括etcd和Consul。
服务注册器的另一个示例是NetflixOSS Prana。它主要面向使用非JVM语言编写的服务,是一个与服务实例并行运行的边车应用程序。Prana在Netflix Eureka中注册和注销该服务实例。
服务注册器是部署环境的内置组件。由自动扩缩组创建的EC2实例可自动注册到ELB。Kubernetes服务会自动注册,并可供发现。
第三方注册模式既有优势,也有不足。其主要优势之一是服务与服务注册中心相互解耦。无需针对开发人员使用的每种编程语言和框架分别实现服务注册逻辑,而是由专用服务集中处理服务实例注册。
此模式的一个缺点是,除非部署环境已内置该模式,否则还需要设置和管理另一个高可用的系统组件。
总结
在微服务应用中,正在运行的服务实例集合会动态变化,实例的网络位置也是动态分配的。因此,客户端必须使用服务发现机制,才能向服务发出请求。
服务发现的一个关键组成部分是服务注册表。服务注册表是一个存储可用服务实例的数据库。服务注册表提供管理API和查询API。服务实例通过管理API在服务注册表中注册和注销。系统组件使用查询API发现可用的服务实例。
服务发现主要有两种模式:客户端发现和服务端发现。在使用客户端服务发现的系统中,客户端查询服务注册表,选择一个可用实例并发出请求。在使用服务端发现的系统中,客户端通过路由器发出请求;路由器查询服务注册表,并将请求转发到可用实例。
服务实例在服务注册表中注册和注销主要有两种方式。一种是由服务实例自行在服务注册表中注册,即“自注册模式”。另一种是由其他系统组件代表服务处理注册和注销,即“第三方注册模式”。
在某些部署环境中,需要使用服务注册表自行搭建服务发现基础设施,例如Netflix Eureka、etcd或Apache Zookeeper。其他部署环境则内置了服务发现功能。例如,Kubernetes和Marathon负责服务实例的注册和注销。它们还会在每个集群主机上运行代理,充当服务端发现路由器。
NGINX等HTTP反向代理和负载均衡器也可用作服务端发现负载均衡器。服务注册中心可将路由信息推送到NGINX,并触发平滑配置更新;例如,可以使用Consul Template。NGINX Plus支持其他动态重新配置机制,可通过DNS从注册中心拉取服务实例信息,并提供用于远程重新配置的API。
在后续博文中,我们将继续深入探讨微服务的其他方面。订阅NGINX邮件列表(表单见下方),即可在本系列后续文章发布时收到通知。
编者:本系列共7篇文章,现已全部发布:
您还可以下载电子书,其中收录了完整系列文章,以及使用NGINX Plus实施微服务的相关信息:微服务:从设计到部署。
客座博主Chris Richardson是早期面向Amazon EC2的Java平台即服务(PaaS)CloudFoundry.com的创始人。目前,他为各类组织提供咨询服务,帮助其改进应用开发和部署方式。他还定期在https://microservices.io上发布有关微服务的博文。
相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
