微服务简介

想象一下,如果你在写代码调用一个有REST API或GRPC API的服务,你的代码需要知道一个服务实例的网络地址(IP地址和端口)。运行在物理硬件上的传统应用中,服务实例的网络地址是相对静态的,你的代码可以从一个很少更新的配置文件中读取网络地址。

在一个现代的,基于云的微服务应用中,这个问题就变得复杂多了,如下图所示:

这里写图片描述

服务实例的网络地址是动态分配的。而且,由于自动扩展,失败和更新,服务实例的配置也经常变化。这样一来,你的客户端代码需要一套更精细的服务发现机制。

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

(1)客户端服务发现模式

当使用客户端服务发现的时候,客户端负责决定可用的服务实例的网络地址,以及围绕他们的负载均衡。客户端向服务注册表(service registry)发送一个请求,服务注册表是一个可用服务实例的数据库。客户端使用一个负载均衡算法,去选择一个可用的服务实例,来响应这个请求,下图展示了这种模式的架构:
这里写图片描述

Dolphin
客户端在启动时直接将服务注册中心etcd中的所有Service加载进内存中,负载均衡算法在客户端中实现,如果注册中心服务注册有变化,如服务增加或减少,客户端是定时load到最新的服务情况。如AService有5个,A1~A5,加载client程序时就将A1~A5对应的服务key-value加载进内存中,由一个负载均衡算法实现并发的访问。

客户端的服务发现模式有优势也有缺点。由于客户端知道可用的服务实例,可以做到智能的,应用明确的负载均衡决策,比如一直用hash算法。这种模式的一个重大缺陷在于,客户端和服务注册表是一一对应的,必须为服务客户端用到的每一种编程语言和框架实现客户端服务发现逻辑。

(2)服务器端服务发现模式

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

HTTP服务器和类似Nginx、Nginx Plus的负载均衡器也可以被用做服务器端服务发现负载均衡器。例如,Consul Template可以用来动态配置Nginx的反向代理。

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

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

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


  • 服务注册表(Service Registry)

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

服务注册的例子包括:

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

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

  • 服务注册(Service Registration)

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

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

在self-registration模式中,服务实例负责从服务注册表中注册和注销。如果需要的话,一个服务实例发送心跳请求防止注册过期。下图展示了这种模式的架构:

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是一个部署环境的内置组件。

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


总结

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

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

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

有两种形式可以实现服务实例的注册和注销,一种是self-registration模式(简单,每个服务都要实现向服务中心注册),一种是third-party registration模式(解耦,由第三方服务轮询check服务变化情况,第三方服务再发起服务注册或变更)。

一些部署环境中,需要通过类似etcd或Apache Zookeeper的组件,启动自己的服务发现基础设施。

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

微服务架构的产品或许会有数百甚至数千个微服务所构成。所以, 部署微服务时, 便很难经由手工来完成, 而必须相当程度的依赖自动化的 DevOps 工具。


服务注册与发现 部署 监控
Zookeeper Cloud Foundry SonarCube
Doozer Gradle Logstash
Etcd Docker New Relic
SmartStack Docker Graphite
Eureka Docker Hub Mesosphere / DCOS
NSQ Docker Machine Winston
Serf Kitematic Hystrix
Spotify Docker Compose
DNS Docker Swarm
SkyDNS AWS
Consul Jenkins
Continuum
Hudson
Artifactory
Grunt
OpenShift