本文编写于 238 天前,最后修改于 238 天前,其中某些信息可能已经过时。

接上篇《3.服务提供者与服务消费者》

下面我们来讲解一下与提供者和消费者息息相关的知识点,就是服务的发现与注册。

一、服务模块沟通的缺陷

回顾一下上一篇我们完成的结构:


上一篇我们使用Spring boot搭建了一个服务提供者和一个服务消费者,也就是一个电影微服务和一个用户微服务。其中电影微服务使用RestTemplate去调用用户微服务提供的接口,那其实这种调用是有一定问题的:

(1)服务地址的变化导致的修改成本
譬如说在现在的部署环境下,这些服务的网路地址可能是变化的,假设用户微服务进行了升级和重新部署,ip以及端口都发生了变化,电影微服务就要进行修改,如果还有其它调用用户微服务的服务,则都需要修改,这种硬编码的模式,维护起来十分麻烦。

(2)多个服务节点的负载
架设用户微服务有多个节点,电影微服务调用的时候肯定需要一个负载均衡的操作,如果使用Nginx或其它可以反向代理的软件的话,其实并不能完全满足我们的需求。
假设类似用户微服务的这种服务提供者有很多种,每一种都有好几个实例,那我们岂不是需要假设很多的Nginx服务器,这里消耗的资源也是巨大的,Nginx的管理也是一个很大的问题。

基于上面的几个问题,提出了一个“服务发现”的概念。什么是服务发现?我们看下面一幅图:


这里,服务消费者和服务提供者在启动的时候,都会把自己的IP和端口,注册到服务发现组件(也可称为注册中心)中去。而服务消费者调用服务时,会从服务发现组件中去查询服务提供者的IP和端口,进行调用。这样就解决了服务地址的变化导致的修改成本,服务发现组件来维护服务实例对应的IP和端口。

还有,当服务提供者Down掉了,那么服务发现组件会将挂掉的服务节点剔除。注册服务会定时向服务发现组件发送心跳,一旦某个服务节点的心跳回馈异常,服务发现组件会剔除该服务。如Dubbo的心跳,以及Eureka就是这样的机制。

二、服务发现组件的功能

上面说的服务发现组件好像看起来很强大的样子,那么它到底具备哪些功能呢?

(1)服务注册表
服务发现组件中有一个重要的信息存储区域,就是“服务注册表”。服务注册表是一个记录当前可用服务实例的网络信息的数据库,是服务发现机制的核心。服务注册表提供查询API和管理API,使用查询API获得可用的服务实例,使用管理API可实现注册和注销。

(2)服务注册和注销
服务注册功能就是将服务添加到服务注册表,而服务注销功能就是将服务从服务注册表中剔除。

(2)健康检查
我们刚刚说的所谓的“心跳机制”,也是一种健康检查的机制。即一定时间内没有达到某种阈值,服务发现组件就认为它是不健康的服务,会影响服务消费者的调用,所以会将其从服务注册表中剔除。

三、服务发现的方式

有关服务发现的方式有两种,一种是“客户端发现”,一种是“服务端发现”。那么“客户端发现”与“服务端发现”分别是什么,它们又有什么区别呢?

下面是世界十大架构师Chris Richardson写的一篇博客的中文译文(原文地址:http://blog.daocloud.io/microservices-4/),通过该译文我们可以了解上面两个概念:

客户端发现模式
使用客户端发现模式时,客户端决定相应服务实例的网络位置,并且对请求实现负载均衡。客户端查询服务注册表,后者是一个可用服务实例的数据库;然后使用负载均衡算法从中选择一个实例,并发出请求。

客户端从服务注册服务中查询,其中是所有可用服务实例的库。客户端使用负载均衡算法从多个服务实例中选择出一个,然后发出请求。

下图显示了这种模式的架构:


服务实例的网络位置在启动时被记录到服务注册表,等实例终止时被删除。服务实例的注册信息通常使用心跳机制来定期刷新。

Netflix OSS 是客户端发现模式的绝佳范例。Netflix Eureka 是一个服务注册表,为服务实例注册管理和查询可用实例提供了 REST API 接口。Netflix Ribbon 是 IPC 客户端,与 Eureka 一起实现对请求的负载均衡。我们会在后面深入讨论 Eureka。

客户端发现模式优缺点兼有。这一模式相对直接,除了服务注册外,其它部分无需变动。此外,由于客户端知晓可用的服务实例,能针对特定应用实现智能负载均衡,比如使用哈希一致性。这种模式的一大缺点就是客户端与服务注册绑定,要针对服务端用到的每个编程语言和框架,实现客户端的服务发现逻辑。

分析过客户端发现后,我们来了解服务端发现。

服务端发现模式
另外一种服务发现的模式是服务端发现模式,下图展现了这种模式的架构:

客户端通过负载均衡器向某个服务提出请求,负载均衡器查询服务注册表,并将请求转发到可用的服务实例。如同客户端发现,服务实例在服务注册表中注册或注销。

AWS Elastic Load Balancer(ELB)是服务端发现路由的例子,ELB 通常均衡来自互联网的外部流量,也可用来负载均衡 VPC(Virtual private cloud)的内部流量。客户端使用 DNS 通过 ELB 发出请求(HTTP 或 TCP),ELB 在已注册的 EC2 实例或 ECS 容器之间负载均衡。这里并没有单独的服务注册表,相反,EC2 实例和 ECS 容器注册在 ELB。

HTTP 服务器与类似 NGINX PLUS 和 NGINX 这样的负载均衡起也能用作服务端的发现均衡器。Graham Jenson 的 Scalable Architecture DR CoN: Docker, Registrator, Consul, Consul Template and Nginx 一文就描述如何使用 Consul Template 来动态配置 NGINX 反向代理。Consul Template 定期从 Consul Template 注册表中的配置数据中生成配置文件;文件发生更改即运行任意命令。在这篇文章中,Consul Template 生成 nginx.conf 文件,用于配置反向代理,然后运行命令,告诉 NGINX 重新加载配置文件。在更复杂的实现中,需要使用 HTTP API 或 DNS 来动态配置 NGINX Plus。

Kubernetes 和 Marathon 这样的部署环境会在每个集群上运行一个代理,将代理用作服务端发现的负载均衡器。客户端使用主机 IP 地址和分配的端口通过代理将请求路由出去,向服务发送请求。代理将请求透明地转发到集群中可用的服务实例。

服务端发现模式兼具优缺点。它最大的优点是客户端无需关注发现的细节,只需要简单地向负载均衡器发送请求,这减少了编程语言框架需要完成的发现逻辑。并且如上文所述,某些部署环境免费提供这一功能。这种模式也有缺点。除非负载均衡器由部署环境提供,否则会成为一个需要配置和管理的高可用系统组件。

其实通过上面的译文可以总结出,“客户端发现”机制就是客户端去从服务注册服务中查询可用的服务实例,使用负载均衡算法选择一个区请求;“服务端发现”机制就是客户端通过负载均衡器向某个服务提出请求,负载均衡器查询服务注册表,并将请求转发到可用的服务实例。

对于“客户端发现”,目前已经有很多成熟的产品,比如Eureka和Zookeeper。
对于“服务端发现”,有Consul+Nginx的方案。

注:我们前面提到的“多个服务节点的负载”的问题,通过“客户端发现”机制,客户端使用负载均衡算法从通过服务发现组件获取的多个服务实例中选择出一个,然后发出请求。

后面我们使用的服务发现组件(也就是注册中心)为Eureka,原因是Spring Cloud目前对Eureka的支持是最完善的,其次是Consul,最差的是Zookeeper。这不是说后面两者的服务发现和注册功能不好,而考量的是在Spring Cloud中对其它组件的配合是不是完善。

四、术语解释

目前市面上有不少微服务的教学书籍和视频,对服务发现组件有很多的叫法,例如“服务注册”、“服务发现”、“注册中心”,其实这些叫法都没有问题,为了统一大家的学习认知,后面我们对类似“注册中心”的服务组件,统一称之为“服务发现组件”。

下一篇我们将着重介绍Eureka服务发现组件,以及搭建一个Eureka的服务发现实例。