如何实现微服务架构中的服务发现

为何使用服务发现?

想象一下,若是你在写代码调用一个有REST API或Thrift API的服务,你的代码须要知道一个服务实例的网络地址(IP地址和端口)。运行在物理硬件上的传统应用中,服务实例的网络地址是相对静态的,你的代码能够从一个不多更新的配置文件中读取网络地址。nginx

在一个现代的,基于云的微服务应用中,这个问题就变得复杂多了,以下图所示:
无服务发现时,没法肯定链接的服务
服务实例的网络地址是动态分配的。并且,因为自动扩展,失败和更新,服务实例的配置也常常变化。这样一来,你的客户端代码须要一套更精细的服务发现机制。算法

有两种主要的服务发现模式:客户端服务发现(client-side discovery)和服务器端服务发现(server-side discovery)。咱们首先来看下客户端服务发现。shell

 

客户端服务发现模式

当使用客户端服务发现的时候,客户端负责决定可用的服务实例的网络地址,以及围绕他们的负载均衡。客户端向服务注册表(service registry)发送一个请求,服务注册表是一个可用服务实例的数据库。客户端使用一个负载均衡算法,去选择一个可用的服务实例,来响应这个请求,下图展现了这种模式的架构:
客户端发现模式
一个服务实例被启动时,它的网络地址会被写到注册表上;当服务实例终止时,再从注册表中删除。这个服务实例的注册表经过心跳机制动态刷新。数据库

Netflix OSS提供了一个客户端服务发现的好例子。Netflix Eureka是一个服务注册表,提供了REST API用来管理服务实例的注册和查询可用的实例。Netflix Ribbon是一个IPC客户端,和Eureka一块儿处理可用服务实例的负载均衡。下面会深刻讨论Eureka。编程

客户端的服务发现模式有优点也有缺点。这种模式相对直接,可是除了服务注册表,没有其它动态的部分了。并且,因为客户端知道可用的服务实例,能够作到智能的,应用明确的负载均衡决策,好比一直用hash算法。这种模式的一个重大缺陷在于,客户端和服务注册表是逻辑耦合,必须为服务客户端用到的每一种编程语言和框架实现客户端服务发现逻辑。缓存

 

服务器端服务发现模式

下图展现了这种模式的架构
服务器端服务发现模式
客户端经过负载均衡器向一个服务发送请求,这个负载均衡器会查询服务注册表,并将请求路由到可用的服务实例上。经过客户端的服务发现,服务实例在服务注册表上被注册和注销。服务器

AWS的ELB(Elastic Load Blancer)就是一个服务器端服务发现路由器。一个ELB一般被用来均衡来自互联网的外部流量,也能够用ELB去均衡流向VPC(Virtual Private Cloud)的流量。一个客户端经过ELB发送请求(HTTP或TCP)时,使用的是DNS,ELB会均衡这些注册的EC2实例或ECS(EC2 Container Service)容器的流量。没有另外的服务注册表,EC2实例和ECS容器也只会在ELB上注册。markdown

HTTP服务器和相似Nginx、Nginx Plus的负载均衡器也能够被用作服务器端服务发现负载均衡器。例如,Consul Template能够用来动态配置Nginx的反向代理。网络

Consul Template按期从存储在Consul服务注册表的数据中,生成任意的配置文件。每当文件变化时,会运行一个shell命令。好比,Consul Template能够生成一个配置反向代理的nginx.conf文件,而后运行一个命令告诉Nginx去从新加载配置。还有一个更复杂的实现,经过HTTP API或DNS去动态地从新配置Nginx Plus。架构

有些部署环境,好比Kubernetes和Marathon会在集群中的每一个host上运行一个代理。这个代理承担了服务器端服务发现负载均衡器的角色。为了向一个服务发送一个请求,一个客户端使用host的IP地址和服务分配的端口,经过代理路由这个请求。这个代理会直接将请求发送到集群上可用的服务实例。

服务器端服务发现模式也是优点和缺陷并存。最大的好处在于服务发现的细节被从客户端中抽象出来,客户端只须要向负载均衡器发送请求,不须要为服务客户端使用的每一种语言和框架,实现服务发现逻辑;另外,这种模式也有一些问题,除非这个负载均衡器是由部署环境提供的,又是另外一个须要启动和管理的高可用的系统组件。

 

服务注册表(Service Registry)

服务注册表是服务发现的关键部分,是一个包含了服务实例的网络地址的数据库,必须是高可用和最新的。客户端能够缓存从服务注册表处得到的网络地址。可是,这些信息最终会失效,客户端会找不到服务实例。因此,服务注册表由一个服务器集群组成,经过应用协议来保持一致性。

正如上面提到的,Netflix Eureka是一个服务注册表的好例子。它提供了一个REST API用来注册和查询服务实例。一个服务实例经过POST请求来注册本身的网络位置,每隔30秒要经过一个PUT请求从新注册。注册表中的一个条目会由于一个HTTP DELETE请求或实例注册超时而被删除,客户端经过一个HTTP GET请求来检索注册的服务实例。

Netflix经过在每一个EC2的可用区中,运行一个或多个Eureka服务器实现高可用。每一个运行在EC2实例上的Eureka服务器都有一个弹性的IP地址。DNS TEXT records用来存储Eureka集群配置,其实是从可用区到Eureka服务器网络地址的列表的映射。当一个Eureka服务器启动时,会向DNS发送请求,检索Eureka集群的配置,定位节点,并为本身分配一个未占用的弹性IP地址。

Eureka客户端(服务和服务客户端)查询DNS去寻找Eureka服务器的网络地址。客户端更想使用这个可用区内的Eureka服务器,若是没有可用的Eureka服务器,客户端会用另外一个可用区内的Eureka服务器。

其它服务注册的例子包括:

  • Etcd:一个高可用,分布式,一致的key-value存储,用来共享配置和服务发现。Kubernetes和Cloudfoundry都使用了etcd;
  • Consul:一个发现和配置服务的工具。客户端能够利用它提供的API,注册和发现服务。Consul能够执行监控检测来实现服务的高可用;
  • Apache Zookeeper:一个经常使用的,为分布式应用设计的高可用协调服务,最开始Zookeeper是Hadoop的子项目,如今已经顶级项目了。

一些系统,好比Kubernetes,Marathon和AWS没有一个明确的服务注册组件,这项功能是内置在基础设置中的。

下面咱们来看看服务实例如何在注册表中注册。

 

服务注册(Service Registration)

前面提到了,服务实例必需要从注册表中注册和注销,有不少种方式来处理注册和注销的过程。一个选择是服务实例本身注册,即self-registration模式。另外一种选择是其它的系统组件管理服务实例的注册,即第third-party registration模式。

自注册模式(The Self-Registration Pattern)

在self-registration模式中,服务实例负责从服务注册表中注册和注销。若是须要的话,一个服务实例发送心跳请求防止注册过时。下图展现了这种模式的架构:
自注册模式
Netflix OSS Eureka客户端是这种方式的一个好例子。Eureka客户端处理服务实例注册和注销的全部问题。Spring Cloud实现包括服务发如今内的多种模式,简化了Eureka的服务实例自动注册。仅仅经过@EnableEurekaClient注释就能够注释Java的配置类

self-registration模式一样也是优劣并存。优点之一在于简单,不须要其它组件。缺点是服务实例和服务注册表相对应,必需要为服务中用到的每种编程语言和框架实现注册代码。

第三方注册模式(The Third-Party Registration Pattern)

在third-party registration模式中,服务实例不会本身在服务注册表中注册,由另外一个系统组件service registrar负责。service registrar经过轮询部署环境或订阅事件去跟踪运行中的实例的变化。当它注意到一个新的可用的服务实例时,就会到注册表中去注册。service registrar也会将中止的服务实例注销,下图展现了这种模式的架构。
第三方注册模式
service registrar的一个例子是开源的Registrator项目。它会自动注册和注销像Docker容器同样部署的服务。Registrator支持etcd和Consul等服务注册。

另外一个service registrar的例子是NetflixOSS Prana。主要用于非JVM语言编写的服务,它是一个和服务实例配合的『双轮』应用。Prana会在Netflix Eureka上注册和注销实例。

service registrar是一个部署环境的内置组件,由Autoscaling Group建立的EC2实例能够被ELB自动注册。Kubernetes服务也能够自动注册。

third-party registration模式主要的优点在于解耦了服务和服务注册表。不须要为每一个语言和框架都实现服务注册逻辑。服务实例注册由一个专用的服务集中实现。缺点是除了被内置到部署环境中,它自己也是一个高可用的系统组件,须要被启动和管理。

 

总结

在一个微服务应用中,服务实例在运行时的配置也会动态变化,包括他们的网络地址。为了知足客户端向服务发送请求的须要,必需要实现服务发现机制。

服务发现的关键部分是服务注册表。服务注册表是一个可用的服务实例的数据库。服务注册表提供了一个管理API和一个查询API。服务实例的注册和注销经过管理API实现,查询API用来寻找可用的服务实例。

有两种主要的服务发现模式:客户端服务发现和服务器端服务发现。客户端服务发现系统中,客户端查询服务注册表,选择一个可用的实例,响应一个请求;在服务器端服务发现系统中,客户端经过一个路由器发送请求,这个路由器会去查询服务注册表,并将请求发送给可用的实例。

有两种形式能够实现服务实例的注册和注销,一种是self-registration模式,一种是third-party registration模式。

一些部署环境中,须要经过相似Netflix Eureka,etcd或Apache Zookeeper的组件,启动本身的服务发现基础设施。其它的部署环境中,服务发现是内置的。好比,Kubernetes和Marathon处理服务实例的注册和注销,还会在每一个集群host上运行一个代理,做为服务器端服务发现路由器的角色。

一个HTTP反向代理和Nginx也能够被用作服务器端服务发现负载均衡器。服务注册表能够推送路由信息到Nginx,引发配置更新,好比能够用Consul Template。Nginx Plus支持动态的重配置机制,能够从注册表中拉取服务实例相关的信息,还提供了远程配置的API。

相关文章
相关标签/搜索