微服务-服务注册与发现

266 阅读10分钟

juejin.cn/post/684490…

juejin.cn/post/684490…

服务发现

本章主要介绍以下内容:

  1. 什么是服务发现?(what)
  2. 微服务框架下为什么需要服务发现呢?(why)
  3. 服务发现是怎么运作的呢?(how)
  4. CAP定理
  5. 现有的几种解决方案介绍 (implementations)

什么是服务发现(what)

服务发现指的是寻找一个service provider的 网络位置信息。

具体指的是,使用一个注册中心来记录分布式系统中全部服务的信息,以便让其他服务能够快速找到这些已经注册的服务。

服务发现是支撑大规模SOA(Service-oriented architecture)和微服务架构的核心模块,需要具有服务注册、服务查找、服务健康检查和服务变更通知等关键功能。

为什么需要服务发现?(why)

因为没有服务发现模块的话,服务网络位置信息的配置会耦合在具体服务消费者的配置当中,从而导致系统难以维护。

想想这么一个基本的问题:服务消费者们是如何知道服务提供者的IP和端口的呢?

在简单的体系架构中,静态配置(比如DNS、Nignx负载均衡配置等)的方法可以很好的解决问题。每个服务都部署在同一个位置,并且很少更改。没有弹性伸缩的需求。传统的单体式应用的网络地址发生变化的概率较小,在发生变化的时候,运维人员手动更新、加载配置文件即可。

但是在微服务架构中并非如此,微服务更新、发布频繁,并且经常会根据负载情况进行弹性伸缩,因为微服务应用实例的网络地址变化是一个很常态的事情,而我们前面提到的静态配置的解决方案,显然不适合这么一个高度动态的场景。所以,我们需要提供一种机制(模块),让服务消费者在服务提供者的IP地址发生变化的时候能够快速及时地获取到最新的服务信息

服务发现是怎么运作的呢?(how)

前面说过,使用一个注册中心来记录分布式系统中全部服务的信息,以便让其他服务能够快速找到这些已经注册的服务。

让我们用两个例子来说明服务发现运作的机制(简单版本):

  1. biz service启动,告诉服务中心它的服务信息,服务中心完成写入
  2. admin service启动,向服务中心请求biz service的服务信息
  3. 服务中心查找对应服务的位置信息,返回给admin service
  4. admin service获取到实际的地址之后,向biz service发起请求

biz service采用集群架构,有更多的节点上线时,整个工作流是怎么样的呢?

  1. 新启动的节点告诉服务注册发现中心自己的服务信息,服务中心完成写入
  2. admin service发起请求更新 biz service的服务地址列表

这里举的是client端主动请求去更新信息,是“pull”的方式;还有一种是client端注册回调,等待服务中心通知,是"push"的方式)

CAP 定理

对于服务发现和注册中心集群来说,如果选择一致性而牺牲可用性(选择CP)的话,那么为了保证多点服务中心上的数据一致,一旦某个点的服务中心宕机,服务中心集群都需要暂停对外提供数据写入服务。在保证服务中心集群的数据一致的同时,牺牲了写入服务的可用性。 如果选择可用性而牺牲一致性(选择AP)的话,那么为了保证服务不中断,当某个点的服务中心宕机时,仍然存活的服务中心节点可以选择先将数据写入本地存储然后直接返回客户端,但这样又将导致多个节点之间的数据不一致。

业界提供的用于服务发现注册的系统,本质上都是满足APCP的系统。

现有的解决方案 (implementations)

在分布式服务体系中,所有的服务提供者和消费者都依赖于【服务中心】,如果服务中心出现问题,将会出现服务状态感知不敏感等现象,且波及整个系统。因此,保证用于服务发现的注册中心的可用性至关重要。 为保证注册中心的可用性,要保证多节点部署,如果是大型网站后台通常还要跨多机房进行部署,以确保注册中心在单一机房不可用的情况下仍然可以提供服务。 具有高可用特性的服务中心需要具备以下几个能力:

  1. 具有多节点部署的能力
  2. 在分布式场景下具备自我治愈和调整的能力
  3. 节点健康检查的能力,可以将访问超时的节点从当前集群中移除,也可以将恢复访问能力后的节点再度加入当前集群

在下面的章节中,我们将介绍几个常见的可直接作为注册中心的产品。

Zookeeper

Zookeeper 致力于提供一个高可用且具有严格顺序访问控制能力的分布式协调系统,它是一个分布式数据一致性的解决方案。

ZooKeeper 提供了分布式通知和协调、 配置管理 、命名服务、主节点选举、分布式锁、分布式 队列 等完善的解决方案。其中分布式通知和协调被广泛用于服务发现。至今为止,它是服务发现领域历史最为悠久、使用最为广泛的产品。

优势与不足

ZooKeeper 作为使用最为广泛的分布式协调组件,优点非常多。使用广泛就是它最大的优点,这也使得ZooKeeper很容易在架构师进行技术选型时占据优势。但是,需要明确说明的是,Zookeeper并不是服务发现领域的最佳选择了,它的优势主要体现在选举和分布式锁等分布式强一致性的场景中。 当ZooKeeper的主节点因为网络故障与其他节点失去联系而触发整个系统选举时,集群是不可用的,这将导致注册服务体系在选举期间瘫痪。

延伸阅读 阿里巴巴为什么不用 ZooKeeper 做服务发现?

服务中心对数据一致性的要求并不是非常苛刻的,也难于做到实时感知宕机(会有时延),它更看重的是自愈能力。通过Zookeeper的客户端Curator的缓存能力能够让ZooKeeper在服务发现领域的适配度更高,但这并非ZooKeeper的原生能力和设计初衷。

etcd

随着CoreOS和Kubernetes等项目在开源社区日益火热,它们项目中都用到的etcd组件作为一个高可用、强一致性的服务发现存储仓库,渐渐走进开发人员的视野。

etcd 是一个受到Zookeeper启发的项目,和其具有类似的架构和功能,基于更为简单易懂的Raft协议和go语言实现。etcd也是一个CP的系统,对一致性的要求强于可用性的要求。etcd通过TTL(Time To Live,存活时间)来实现类似于Zookeeper临时节点的功能,需要etcd客户端不断地定时续租节点来判断服务的运行状态。

具体可以阅读下面的延伸文章。

延伸阅读 etcd:从应用场景到实现原理的全方位解读

官网:etcd.io/

再介绍一篇高质量的etcd实现原理解读的文章:高可用分布式存储 etcd 的实现原理

相比Zookeeper,etcd还具有以下优点:

  1. 简单。使用 Go 语言编写部署简单;使用 HTTP 作为接口使用简单;使用 Raft 算法保证强一致性让用户易于理解
  2. 数据持久化。etcd 默认数据一更新就进行持久化。
  3. 安全。etcd 支持 SSL 客户端安全认证。

Consul

Consul 来源于HashiCorp的产品,提供了一些列特性,包括服务发现 、更丰富的健康检查(内存、磁盘使用情况等 细粒度 服务状态检测功能)、键值对存储功能以及多 数据中心(官网描述的四个主要特色)。和其他方案比较起来,具有“一站式”的特点。

Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。

和etcd一样, Consul基于 raft 协议,要求必须过半数的节点都写入成功才认为注册成功 Leader 挂掉时,重新选举期间整个 Consul 不可用。保证了强一致性但牺牲了可用性。

负载均衡 算法基础

下面我们继续看看服务消费者应当如何通过合理的负载均衡算法得到合适的服务节点呢?在此之前,我们先来了解下负载均衡算法的基础知识。

服务消费者在发起 RPC 调用之前,需要感知有多少服务端节点可用,然后从中选取一个进行调用。之前我们提到了几种常用的负载均衡策略:Round-Robin 轮询、Weighted Round-Robin 权重轮询、Least Connections 最少连接数、Consistent Hash 一致性 Hash 等。本节课我们讨论的主角是基于一致性 Hash 的负载均衡算法,一致性 Hash 算法可以保证每个服务节点分摊的流量尽可能均匀,而且能够把服务节点扩缩容带来的影响降到最低。下面我们一起看下一致性 Hash 算法的设计思路。

在服务端节点扩缩容时,一致性 Hash 算法会尽可能保证客户端发起的 RPC 调用还是固定分配到相同的服务节点上。一致性 Hash 算法是采用哈希环来实现的,通过 Hash 函数将对象和服务器节点放置在哈希环上,一般来说服务器可以选择 IP + Port 进行 Hash,如下图所示。

图中 C1、C2、C3、C4 是客户端对象,N1、N2、N3 为服务节点,然后在哈希环中顺时针查找距离客户端对象 Hash 值最近的服务节点,即为客户端对应要调用的服务节点。假设现在服务节点扩容了一台 N4,经过 Hash 函数计算将其放入到哈希环中,哈希环变化如下图所示。

此时 N2 和 N4 之间的客户端对象需要重新进行分配,可以看出只有 C3 会被分配到新的节点 N4 上,其他的都保持不变。服务节点下线与上线的处理过程是类似的,你可以自行分析下服务节点下线时哈希环是如何变化的。

如果服务节点的数量很少,不管 Hash 算法如何,很大可能存在服务节点负载不均的现象。而且上图中在新增服务节点 N4 时,仅仅分担了 N1 节点的流量,其他节点并没有流量变化。为了解决上述问题,一致性 Hash 算法一般会引入虚拟节点的概念。如下图所示。

图中相同颜色表示同一组虚拟服务器,它们经过 Hash 函数计算后被均匀放置在哈希环中。如果真实的服务节点越多,那么所需的虚拟节点就越少。在为客户端对象分配节点的时候,需要顺时针从哈希环中找到距离最近的虚拟节点,然后即可确定真实的服务节点。