译者按
还是推荐大家尽量读原文。
服务发现机制并不是什么新东西了,只是随着微服务架构的流行,其变得越来越普遍和重要。本文是一片不错的介绍性文章,对于高手们来说,其中没有太多新鲜的东西。但是里面提到的大部分技术都依旧在快速地发展,时刻关注它们的变化,对于正在从事此项工作的同学来说还是很重要的。
本文介绍了服务发现的发现和注册两个机制的实现模式,其中的优缺点部分讲的我认为是比较简单的。但是在实际工作中,其方案的选择要远比这要复杂。同时,对于大规模的应用,其实现方式更倾向于多种模式的组合使用。
我在译文里加了些注释,如有错误,请多多指出。
下面是分割线
**原文地址:**https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/
这是应用微服务构建应用文章系列的第四篇。第一篇文章介绍微服务结构模式和讨论微服务优缺点。第二篇和第三篇是描述微服务通讯的不同方面。本系列,我们来探索与服务发现紧密相关的问题。
为什么要服务发现?
让我们想象一下你在一些调用 REST 或 Thrift 服务的代码。为了能请求成功,你的代码需要知道服务实例的网络地址(IP 和端口)。在传统的运行在物理机上的应用,服务的网络地址是相对静态的。例如,你的代码可以从偶尔更新的配置文件中读取到网络地址。
然而,在现代的基于云计算的微服务应用中,问题将变得难以解决,如下图所示:
服务实例会被动态地分配网络地址。并且,因为自动伸缩、故障和升级,服务实例会动态地改变。故而,你的客户端代码需要用一种更加精密的服务发现机制。
有两个主要的服务发现模式:客户端发现和服务端发现。让我们先来看看服务端发现。
客户端发现模式
当使用客户端发现模式的时候,客户端负有决定可用的服务实例和负载均衡之职责。客户端查询服务注册中心。所谓服务注册中心就是一个保存可用服务实例的数据库。然后,客户端使用负载均衡算法选择其中一个可用的服务实例并向其发出请求。
下图展示了这种模式的结构
当服务实例启动时,其会将自己的网络地址注册到服务注册中心。在实例销毁时删除这些信息。服务实例的注册信息会用心跳机制定期刷新。
Netflix OOS 提供了一个非常好的客户端服务发现模式的例子。Netflix Eureka _(注:希腊语“我发现了”的意思)_是一个服务注册中心。提供了用于服务实例注册管理和查询可用实例的 REST API。Netflix Ribbon 提供了能够和 Eureka 配合使用的进程间通信客户端,其可以实现请求的负载均衡。我们将在稍后深入讨论 Eureka。
客户端发现模式有利有弊。这个模式相对直接,并且,除了服务注册中心以外,没有其它活动(moving)部分。而且,因为客户端了解可用的服务实例,因此可以实现智能的,与应用相关的负载均衡策略,例如一致性哈希。(但是其)一个明显的缺点是将客户端和服务注册中心耦合在一起_(注:其实也算不上耦合,如果用 REST 这样的平台无关的协议)_。你必须为你使用的每种语言和框架实现客户端发现逻辑。
现在我们已经看过了客户端发现模式,下面我们来看服务端发现模式。
服务端发现模式
另一个种服务发现的方式是服务端发现模式。如下图所示:
客户端通过负载均衡器向服务发起请求。负载均衡器查询服务注册中心,将每个请求路由到可用的服务实例上。如同客户端发现,服务实例也会在服务注册中心上注册和解注册。
AWS Elastic Load Balancer (ELB)是一个服务端发现路由的例子。ELB 通常用来均衡来自互联网的请求负载。当然,你也可用处理从内网到 VPC 的请求_(注:内部网络,可能是私有云,与 AWS 公有云结合使用的场景)_。客户端使用 ELB 的 DNS 名字向 ELB 发送请求。ELB 在 EC2 实例或 ECS 容器间均衡负载。这里并没有一个分离的服务注册中心。取而代之的做法是 EC2 和 ECS 直接注册到 ELB 上。
如同 Nginx 这样的 HTTP 服务器和负载均衡器也可被用作服务器端发现中的负载均衡器。例如,这篇博文描述了使用 Consul Template (注:Consul 类似于 Zookeeper 和 Etcd 但采用了更轻量的一致性算法)动态配置 Nginx 反向代理。Consul Template 会定期根据 Consul 服务注册中的数据重新生成任意(注:任意的意思是可配)配置文件。并在文件改变的时候执行任意的命令。在这个博文的例子中,Consul Template 生成 nginx.conf 文件,其用来配置反向代理,然后(Consul Template)运行命令告诉 Nginx 重载配置。一个更高级的实现还能动态地使用 HTTP API 或 DNS 配置 Nginx Plus(注:DNS 怎么配置 Nginx,弱弱地我表示不懂)
一些部署环境使用 K8s 和 Marathon 在集群中的每一个宿主机上运行一个代理。代理起到了服务发现模式中的负载均衡器。为了使请求能到服务上,一个客户端使用宿主的 IP 地址和给服务分配的端口,通过代理路由请求(注:宿主 IP 代表代理,端口代表服务)。然后代理透明地将请求发送到这个集群中某个可用的服务实例上。
服务端发现模式有利有弊。一个最大的好处是(服务)发现的细节被从客户端抽象出来。客户端只需简单地向负载均衡器发送请求。这避免了为客户端使用的不同语言和框架实现发现逻辑。并且,就像上面所提到的那样,一些部署环境已经免费滴提供了这个功能。当然,这个模式也有一些缺点。除非部署环境提供了负载均衡器,否则你就需要建立和管理另一套高可用的提供组件。
服务注册中心
服务注册中心是服务发现的关键部分。它是一个包含服务实例网络地址的数据库。一个服务注册中心需要保证高可用和及时更新。客户端可以缓存从服务注册中心获得的网络地址。然而,这些信息迟早会变得过时,从而使得客户端无法发现服务实例。因此,由一个集群组成的服务注册中心使用复制协议(注:Paxos 之类)保证一致性。
我们前面提到了,Netflix Eureka 是一个不错的服务注册中心例子。它提供了相关的 REST API。服务实例可以使用 POST 请求注册网络地址。每30秒必须通过 PUT 请求刷新注册。一个注册可以通过 HTTP DELETE 请求或超时被删除。如你所愿,客户端可以通过 GET 请求获取注册的服务实例。
Netflix 通过在每个 AWS EC2 Availability zone 运行一个或多个 Eureka 服务实现 高可用。每个运行在 EC2 实例上的 Eureka 服务使用一个弹性IP地址 (注:一个基于 DNS 的 AWS 服务)。DNS TEXT 用来记录 Eureka 集群的配置,这个配置是一个从 availability zones 到 Eureka 服务器网络地址的映射_(注:说白了就是域名服务)_。当一个 Eureka 服务启动,它将查询 DNS 以获得 Eureka 集群配置,定位自己的 peers,将自己分配到一个未使用 Elastic IP 地址(注:就是 DHCP 呗?说的不对请纠正我)。
Eureka 客户 - 服务和服务客户端 - 通过查询 DNS 发现 Eureka 服务器的网络地址。客户端倾向使用同一个 Avaiability zones AZ 中的 Eureka 服务。然而,当同一个 AZ 中的 Eureka 不可用时,客户端将使用其它 AZ 的 Eureka_(注:应该存在 DNS 延时性的问题)_。
其它服务注册中心的例子包括_(注:Spring 和 Netflix 应该是真爱)_:
- etcd - 一个高可用,分布式,一致性的 KV 存储,用来共享配置和服务发现。两个使用 etcd 的重量级项目是 Kubernetes(k8s)和 Cloud Foundry。
- consul - 一个服务发现和配置的服务。它提供 API 使得客户端可以注册和发现服务。Consul 可以执行健康检查已决定服务是否可用。
- Apache Zookeeper - 一个被广泛使用的,高性能的,用于协调分布式应用的服务。最初,Zookeeper 是 Hadoop 的子项目,但现在是 Apache 的顶级项目。
如之前提到的,一些系统,如 k8s、Marathon 和 AWS 没有提供显式的服务注册功能。但是,事实上它们的基础服务已经内建了这项功能。
现在我们已经看到了服务注册的概念,接下来让我们来看看服务实力是如何注册的。
服务注册选项
如前所述,服务实例在服务注册中心注册和解注册。实现这两者有两种不同的方式。其一是自行注册模式。其二是第三方注册模式,即由其他系统逐渐管理服务的注册。我们先来看前者。
自行注册模式
当我们使用自行注册模式时,一个服务实例负有将自己注册和解注册给服务注册中心的职责。并且,如果需要,服务实例需要发送心跳请求,以防止注册过期。下图展示了这种模式的结构:
这种方法的一个例子是 Netflix OSS Eureka client。一个 Eureka 客户端搞定服务注册和解注册的方方面面。Spring Cloud 项目,其实现了不同的模式,包括服务发现,使得使用者可以轻松地注册服务实例进 Eureka。你可以简单地在 Java Configuration _(注:Spring 的最新配置方法,可用来替代 XML 配置)_类上加上 @EnableEurekaClient
annotation。
下面说这种模式的优缺点。一个好处是它相对简单,不需要其它系统组件。然而,一个主要缺点是其耦合了服务实例和服务注册中心。你需要为你所使用的每种语言和框架写代码实现注册功能。
(注:另一个好处是可以实现更丰富的服务状态,而不只是可用、不可用这么简单的几种。还能实现比如“哎呦,还不错啊”状态。因为毕竟自己最懂自己)
下面要介绍的第三方注册模式解耦了服务和服务注册中心。
第三方注册模式
当使用第三方注册模式时,服务实例不用负责服务注册功能。取而代之的是,另一个可称为服务注册器的系统组件负责服务注册。服务注册器通过轮询或订阅事件的方式跟踪一组服务实例(状态)的改变。当服务注册器发现新的可用服务后,其将这个实例注册到服务注册中心。服务注册器也可以将销毁的实例解注册。下图展示了这种模式的结构:
服务注册器的一个例子是开源项目 Registrator。它自行地将以 Docker 容器形式运行的服务注册和解注册。Registrator 支持多种服务注册中心,包括 etcd 和 Consul。
另一个服务注册器的例子是 Netflix OSS Prana。其主要用于非 JVM 语言的服务,它是一个附加(sidecar,挎斗车)应用,伴随服务实例运行。Prana 会将服务实例注册和解注册进 Netflix Eureka。
服务处测器是部署环境的内建组件。被 AWS Autoscaling Group 创建的 EC2 实例会自动注册进 ELB。Kubernetes 服务会被自动注册使其可被发现。
优缺点环节。这种模式的主要优点是将服务实例和服务注册中心解耦。你不需要为不同语言和框架实现服务注册逻辑。取而代之的是服务注册将通过一个专门的服务,按照一种集中化的方式进行。
一个缺点是,除非部署环境内建支持,否则你需要另一个建立和管理另一个高可用的系统组件_(注:这个相对还好)_
总结
在微服务应用中,一组运行的服务实例会动态地变化。实例可以被动态分配网络地址。因此,为了使客户端可以成功请求服务,服务发现机制是必须的。
服务发现的一个关键部分是服务注册中心。服务注册中心是一个可用服务实例的数据库。服务注册中心提供注册管理和查询的 API。服务实例通过调用注册 API 注册和解注册。查询 API 用来发现可用的服务实例。
服务发现的两种模式:客户端发现和服务端发现。在一个使用客户端发现模式的系统中,客户查询服务注册信息,选择可用的服务实例,发送业务请求。在一个使用服务端发现模式的系统中,客户通过路由器发送业务请求,路由器查询服务注册信息并投递请求至可用的实例。
服务注册同样有两种方式。一是自行注册模式。另一个第三方注册模式。
在一些部署环境中,我们需要通过使用例如 Netflix Eureka、etcd 或 Zookeeper 建立我们自己的服务发现基础架构。在另一些环境中,服务发现机制是内建的,例如 k8是和 Marathon 处理服务注册和解注册。它们同样在每个集群宿主机上运行代理,起到服务端发现模式中路由器的作用。
一个 HTTP 反向代理和负载均衡器,例如 Nginx 也可以被用来做服务端发现模式中的负载均衡器。服务注册可以推送路由信息给 Ngnix 并更新配置。例如,你可以使用 Consul Template。Nginx Plus 支持额外的动态重配置机制 - 其可用来从 DNS 拉取服务注册的信息,并提供 API 用于远程重配置。
在未来的博文中,我们将深入探索微服务的其它方面。