微服务入门系列:微服务架构的服务发现

这是下面七篇系列文章的第四篇:

  1. 微服务入门系列:微服务介绍
  2. 微服务入门系列:使用 API 网关
  3. 微服务入门系列:微服务架构的进程间通信
  4. 微服务入门系列:微服务架构的服务发现(本文)
  5. 微服务入门系列:基于事件驱动的数据管理
  6. 微服务入门系列:微服务的部署策略选择
  7. 微服务入门系列:单体应用的微服务重构

出自微服务部落:https://blog.idevfun.io/service-discovery-in-a-microservices-architecture/
原文链接:https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/
作者:Chris Richardson
翻译:董干

这 7 篇文章是很不错的微服务入门系列文章,介绍了微服务所需要的主要技术,是当年(本系列文章写于2015年,但现在仍然不过时)带领译者入门微服务的文章,强烈建议深入微服务架构之前先了解本系列。


本文是关于使用微服务架构构建应用的七篇系列文章的第四篇。第一篇介绍了微服务架构模式,同单体架构模式进行了对比,并讨论了微服务的优势和缺点。第二篇第三篇分别从不同角度描述了微服务架构的通信问题。在本文中,我们会探索与服务发现密切相关的问题。

为什么使用服务发现?

假设你正在写代码调用某个提供了 REST API 或者 Thrift API 的服务。为了发起调用,你的代码需要知道服务实例的位置(IP 地址和端口号)。运行在物理硬件上的传统应用服务实例的位置相对比较固定。例如,你可以在代码中通过从偶尔更新的配置文件中读取网络位置。
然而对于现代基于云的微服务应用,这是个要困难得多的问题,如下图所示。

Richardson-microservices-part4-1_difficult-service-discovery

上图中,服务的实例都是动态分配的网络地址。而且,服务实例由于自动扩缩容、故障和升级等,也在不断动态变化。因此,客户端代码需要使用一种更为精细的服务发现机制。

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

客户端发现模式

当使用客户端发现时,客户端负责确定可以用的服务实例的网络地址,并对到这些实例的请求进行负载均衡。客户端查询服务注册中心,即可用服务实例的数据库。然后客户端使用某种负载均衡算法选择其中一个可用的服务实例,并发起请求。

下图描述了这种模式的结构。

Richardson-microservices-part4-2_client-side-pattern

当一个服务实例启动时,它的网络地址注册到注册中心。当这个服务实例终止时,它将从注册中心中注销。服务实例的注册状态通常会使用例如心跳机制进行定期更新。

Netflix OSS 提供了一个客户端发现模式的很好的例子。其中 Netflix Eureka 是个服务注册中心。它提供了用于管理服务实例注册和查询可用实例的 REST API。Netflix Ribbon 是一个远程调用客户端,可以配合 Eureka,将到这些服务的请求负载均衡到可用的实例。本文后面会再深入讨论 Eureka。

客户端发现模式有很多优点和缺点。这种模式相对来说比较直接,除了注册中心,没有其他更多部件。而且,由于客户端知道所有可用的服务实例,它可以做出智能的,应用程序相关的负载均衡策略,例如使用一致性哈希等。这种模式一个主要的缺点是它将客户端同注册中心耦合在一起。你必须对你服务客户端的每种语言和框架都实现一遍客户端服务发现的逻辑。

现在我们了解了客户端发现,我们再来看一下服务端发现。

服务端发现模式

服务发现的另一种方法是服务端发现模式。下图描述了这种模式的结构。

Richardson-microservices-part4-3_server-side-pattern

客户端通过负载均衡器请求到服务。负载均衡器查询服务注册中心,并将每个请求路由到一个可用的服务实例中。同客户端发现一样,服务实例通过注册中心进行注册和注销。

AWS 的弹性负载均衡器(ELB)是服务端服务发现路由器的一个例子。ELB 通常用来对来自外部公网的请求流量进行负载均衡。然而,你也可以使用 ELB 来对处于虚拟私有网络(VPC)的内网的请求流量进行负载均衡。客户端使用 DNS 名称通过 ELB 发起(HTTP 或 TCP)请求。ELB 将流量负载均衡到一些已注册的虚拟主机 EC2 或虚拟容器 ECS 中。这种情况下不需要独立的服务注册中心,而是依靠 EC2 实例和 ECS 容器将自身注册到 ELB 中。

HTTP 服务器和负载均衡器,例如 NGINX Plus 和 NGINX 也可以用来做服务端发现中的负载均衡器。例如,这篇文章描述了如何使用 Consul Template 来动态配置 NGINX 反向代理。Consul Template 是个能够通过读取 Consul 服务注册中心中存储的数据定期生成任意的 NGINX 配置文件的工具。当配置文件改变时,它触发一个 shell 命令。在上面文章中提到的例子中,Consul Template 生成 nginx.conf 配置文件,里面包含对服务实例的反向代理,然后运行一个命令告知 NGINX 重新加载配置。更复杂的实现可以是通过使用 HTTP API 或 DNS 来动态重新配置 NGINX Plus

一些部署环境,例如 KubernetesMarathon,在集群的每个 host 上运行一个 proxy。这个 proxy 扮演着服务端发现负载均衡器的角色。当对某个服务发起请求时,客户端使用主机的 ip 地址和服务分配的端口请求到 proxy,然后 proxy 透明地将请求转发到集群中某个可用的服务实例中。

服务端发现模式有很多优缺点。这个模式一个很大的优点是将服务发现的细节从客户端抽象剥离。客户端仅仅需要请求负载均衡器。这省去了对服务客户端使用的每种语言和框架都实现服务发现逻辑的麻烦。而且,正如上面提到的,一些部署环境本身自带了这种功能。但是,这种模式也有一些缺点。除非负载均衡器是由部署环境提供的,否则这仍然是用户需要配置和管理的另一个有高可用需求的组件。

服务注册中心

服务注册中心是服务发现的关键部分。它是一个包含服务实例网络位置的数据库。服务注册中心应该是个高可用并时刻保持最新的服务。客户端可以缓存从服务中心获取得到的网络位置,然而,这些信息最终会过期从而使得客户端不能发现服务实例。因此,服务注册中心由一组能够通过使用某种数据同步协议来维持一致性的服务器组成。

前面提到的 Netflix Eureka 就是服务注册中心的一个很好的例子。它提供注册和查询服务实例的 REST API。服务实例通过发起一个 POST 请求注册自己的网络位置。每隔 30 秒,它必须通过发起一个 PUT 请求来刷新它的注册状态。服务的注销可以通过主动发起一个 HTTP DELETE 请求或是实例注册状态超时来完成。正如你期望的,客户端可以通过发起一个 HTTP GET 请求获取注册服务的实例。

Netflix 通过在每个 Amazon EC2 可用区中运行一个或多个 Eureka 服务来达到高可用。每个 Eureka 服务运行在一个拥有 弹性 IP(Elastic IP)地址的 EC2 实例上。Eureka 集群的配置信息通过 DNS TEXT 记录来保存,映射从可用区到每个可用区的 Eureka 服务的网络地址。当一个 Eureka 服务启动时,它从 DNS 中获取到 Eureka 集群的配置,定位到其他的节点,分配给自己一个未使用的弹性 IP 地址。

Eureka 客户端 —— 服务和服务的客户端 —— 通过查询 DNS 发现 Eureka 服务的地址。客户端通常倾向于使用在同一个可用区的 Eureka 服务。但当本可用区的 Eureka 服务不可用时,客户端会使用其他可用区的 Eureka 服务。

服务注册中心除此之外还有其他一些例子:

  • etcd —— 一个高可用、分布式的、高一致性的 key-value 存储,用来共享配置和服务发现。使用 etcd 有两个著名项目,Kubernetes 和 Cloud Foundry
  • consul —— 一个用来发现和配置服务的工具。它提供允许客户端注册和发现服务的 API。Consul 还可以通过进行健康检查来确定服务的可用性。
  • Apache Zookeeper —— 一个广泛应用于分布式应用的高性能协调者服务。Apache Zookeeper 原是 Hadoop 的一个子项目,现今已经成为一个顶级项目。

另外,正如前面提到的,一些系统例如 Kubernetes、Marathon 和 AWS,它们并没有明确的服务注册中心。它们的注册中心是基础设施内建的一部分。

现在我们已经了解了服务注册的概念,接下来我们看一下服务实例是如何在服务注册中心注册的。

服务注册的方式

前面提到服务实例必须在服务注册中心中注册和注销。处理注册和注销过程有很多种不同的方式。其中的一种是服务实例自己注册自己,即 自注册模式。另一种方式是使用其他一些系统组件来管理服务实例的注册,即 第三方注册模式。我们先来看一下自注册模式。

自注册模式

当使用 自注册模式 时,服务实例自身负责从服务注册中心注册和注销。并且,有需要的话服务实例通过发送心跳请求来避免注册状态过期。下图展示了这种模式的结构:

Richardson-microservices-part4-4_self-registration-pattern

这种方式的一个很好的例子是 Netflix OSS 套件中的 Eureka 客户端。Eureka 客户端处理服务实例注册和注销所有方面的细节。Spring Cloud 项目 实现了各种服务发现的模式,使得自动注册到 Eureka 变得很简单。你仅仅需要在 Java Configuration 类中标注 @EnableEurekaClient 注解即可。

自注册模式有优势也有不足。一个优势是它相对比较简单,不需要其他的系统组件。但是,一个主要的缺陷是它将服务实例耦合到服务注册中心的实现中。你必须在你所有服务使用的每种语言和框架中都实现一遍服务注册代码。

另一种方式可以将服务和注册中心解耦开来,它就是第三方注册模式。

第三方注册模式

当使用 第三方注册模式 时,服务实例不负责将他们自身注册到服务注册中心,而是通过一个称为*服务注册者(service registrar)*的系统组件来处理服务注册。服务注册者通过轮询部署环境或者通过订阅事件来确定运行的服务实例的变化。当它注意到新的可用服务实例时,它将这个实例注册到注册中心。另外服务注册者也会注销终止的服务实例。下图表示了这种模式的结构。

Richardson-microservices-part4-5_third-party-pattern

开源项目 Registrator 是服务注册者的一个例子。它可以自动将部署为 Docker 容器的服务实例进行服务注册和注销。Registrator 支持包括 etcd 和 Consul 的多种服务注册中心。

另一个服务注册者的例子是 Netflix OSS 套件中的 Prana。Prana 主要为非 JVM 语言的服务而生,它通过 sidecar 应用的形式和服务实例运行在一起。Prana 从 Netflix Eureka 中注册和注销服务实例。

服务注册者是部署环境的内建组件。通过自动扩容组创建的 EC2 实例可以自动注册到 ELB。Kubernetes 的服务可以自动注册并且用于服务发现。

第三方注册模式有优点也有缺点。一个主要的优势是服务同服务注册中心解耦开来,你不需要为开发者使用的每种编程语言和框架都实现一遍服务注册的逻辑,而是通过一个专门的服务以中心化的方式来处理服务实例的注册。

这种模式的一个缺陷是,除非它是部署环境内置的设施,它仍是一个需要你来搭建和管理的高可用系统组件。

总结

在微服务应用中,运行中的服务实例是会动态变化的。服务实例分配动态的网络地址。因此,客户端为了对一个服务发起请求,它必须使用某种服务发现机制。

服务发现的关键组件是服务注册中心。服务注册中心是可用服务实例的数据库。服务注册中心通常会提供管理 API 和查询 API。服务实例通过使用管理 API 来进行注册和注销。查询 API 由系统组件来调用,从而发现可用的服务。

服务发现模式主要分两类:客户端发现和服务端发现。在使用 客户端服务发现模式 的系统中,客户端查询注册中心,选择一个可用的服务实例并发起请求。在使用 服务端发现模式 的系统中,客户端通过路由服务来发起请求,路由服务查询服务注册中心,并将请求转发到可用的实例上。

服务实例注册到注册中心主要有两种方式:一种方式是服务实例自己将自身注册到服务注册中心,称为 自注册模式。另一种方式用某种系统组件来代表服务实例进行服务的注册和注销,称为 第三方注册模式

在某些部署环境中,你需要使用某种服务注册中心,例如 Netflix Eurekaetcd 或者 Apache Zookeeper,来自己搭建服务发现的基础设施。在其他的部署环境中,服务发现可能是内置机制。例如,Kubernetes 和 Marathon 自身可以处理服务实例的注册和注销。它们也会在每个集群的主机上运行一个代理服务用来作为 服务端发现 的路由服务。

HTTP 反向代理和负载均衡器,例如 NGINX,也可以用来作为服务端发现的负载均衡器。服务注册中心可以将路由信息推送给 NGINX 并触发一个优雅的配置更新;例如,你可以使用 Consul Template。NGINX Plus 支持额外的动态重新配置机制 —— 它可以通过 DNS 从注册中心拉取服务实例的信息,并提供远程重新配置的 API。

在接下来的文章中,我们会继续深入到微服务的其他一些方面。


这是下面七篇系列文章的第四篇:

  1. 微服务入门系列:微服务介绍
  2. 微服务入门系列:使用 API 网关
  3. 微服务入门系列:微服务架构的进程间通信
  4. 微服务入门系列:微服务架构的服务发现(本文)
  5. 微服务入门系列:基于事件驱动的数据管理
  6. 微服务入门系列:微服务的部署策略选择
  7. 微服务入门系列:单体应用的微服务重构

出自微服务部落:https://blog.idevfun.io/service-discovery-in-a-microservices-architecture/
原文链接:https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/
作者:Chris Richardson
翻译:董干

这 7 篇文章是很不错的微服务入门系列文章,介绍了微服务所需要的主要技术,是当年(本系列文章写于2015年,但现在仍然不过时)带领译者入门微服务的文章,强烈建议深入微服务架构之前先了解本系列。

Show Comments

Get the latest posts delivered right to your inbox.