菜鸟笔记
提升您的技术认知

服务注册与服务发现简介

阅读 : 1723

在传统的系统部署中,服务运行在一个固定的已知的 IP 和端口上,如果一个服务需要调用另外一个服务,可以通过地址直接调用。但是,在微服务架构下,服务实例的启动和销毁是很频繁的,服务地址在动态的变化,而且,由于自动扩展,失败和更新,服务实例的配置也经常变化,所以,无法通过硬编码服务地址的方法来访问该服务。

服务注册与服务发现简介

因此,需要设置专门的服务来对实时变化的服务状态进行同步。

目前微服务的服务发现机制主要包含三个角色:服务提供者、服务消费者和服务注册表

服务提供者(Service Provider):服务启动时将服务信息注册到服务注册表,服务退出时将服务注册表的服务信息删除掉。
服务消费者(Service Consumer):从服务注册表获取服务提供者的最新网络位置等服务信息,维护与服务提供者之间的通信。
服务注册表(Service Registry):联系服务提供者和服务消费者的桥梁,维护服务提供者的最新网络位置等服务信息。

服务注册与服务发现简介

服务发现机制的关键部分是服务注册表(Service Registry)。服务注册表提供管理和查询服务注册信息的API。当服务提供者的实例发生变更时(新增/删除服务),服务注册表需要通知服务消费者同步最新的服务实例地址列表。目前大多数的微服务框架使用Netflix Eureka、Etcd、Consul或Apache Zookeeper等作为服务注册表。

服务注册(Services Registration)
自注册模式
服务实例本身负责从服务注册表中注册和注销服务,必要时发送心跳防止注册过期。

服务注册与服务发现简介

优点就是简单,不需要其它组件的参与;缺点在服务实例与服务注册表相对应,必须要为服务中用到的每种编程语言和框架实现服务注册的代码。

第三方注册模式
服务实例由另一个系统组件(Registrar)负责注册。Registrar通过轮询部署环境或订阅事件去跟踪运行中的实例的变化,并且向服务注册表注册或者注销服务。

服务注册与服务发现简介

优点是解耦了服务和服务注册表,不需要为每个语言和框架都实现服务注册逻辑。服务实例注册由一个专用的服务集中实现。

缺点是除了被内置到部署环境中,它本身也是一个高可用的系统组件,需要被启动和管理。

服务发现(Services Discovery)
客户端发现模式
客户端负责确定服务提供者的可用实例地址列表和负载均衡策略。客户端访问服务注册表,定时同步目标服务的实例地址列表,然后基于负载均衡算法选择目标服务的一个可用实例地址发送请求。

服务注册与服务发现简介

当服务客户端调用目标服务时,它会查询服务注册表以获取服务实例地址列表。为了提高性能,客户端缓存服务实例地址列表。然后,服务客户端使用负载均衡算法(如循环或随机)来选择服务实例发送请求。优点:

通常是服务客户端查询目标服务的实例地址列表之后,执行负载均衡算法选择可用的目标服务。优点是服务客户端可以灵活、智能地制定负载均衡策略,包括轮询、加权轮询、一致性哈希等策略。
可以实现点对点的网状通讯,即去中心化的通讯。可以有效避开单点造成的性能瓶颈和可靠性下降等问题。
服务客户端通常以SDK的方式直接引入到项目,这种方式语言的整合程度最佳,程序执行性能最佳,程序错误排查更加容易。
缺点:

服务客户端与服务注册表耦合。需要为服务客户端使用的每种编程语言和框架实现客户端服务发现逻辑。
服务客户端通常以SDK方式使用服务发现功能。这种侵入式方案存在于应用程序的所有客户端,如果客户端服务发现功能需要进行更新,要求所有的应用程序重新编译,部署服务。微服务的规模越大,服务更新越困难,这在一定程度上违背了微服务架构提倡的技术独立性。
服务端发现模式
使用服务端发现模式,服务客户端通过路由器(或者负载均衡器)访问目标服务。路由器负责查询服务注册表,获取目标服务实例的地址列表转发请求。

服务注册与服务发现简介

优点:

部署平台提供服务发现功能,负责处理服务发现的所有方面。因此,无论使用任何语言,所有的服务提供者和消费者都可以轻松地使用服务发现机制。
服务发现功能对于服务客户端而言是透明的,因此,服务发现功能的相关更新对于服务客户端是无感知的。
缺点:

部署平台的服务发现功能仅支持发现使用该平台部署的服务。例如,基于Kubernetes 的服务发现仅适应于在Kubernetes上部署运行的服务。
服务的架构增加了一次转发,延迟时间会增加。整个系统增加了一个故障点,系统的运维难度增加。最关键的是负责转发请求的路由器或者负载均衡器可能变成性能的瓶颈。
微服务的一个目标是故障隔离,将整个系统切割为多个服务共同运行,如果某服务无法正常运行,只会影响到整个系统的相关部分功能,其它功能能够正常运行,即去中心化。然而,服务端发现模式实际上是集中式的做法,如果路由器或者负载均衡器无法提供服务,那么将导致整个系统瘫痪。
服务注册表(Services Registry)
服务注册表是一个可用的服务实例的数据库。服务注册表提供了一个管理API和一个查询API。服务实例的注册和注销通过管理API实现,查询API用来寻找可用的服务实例。服务注册表是一个分布式的kv数据库,因此,存在CAP问题。根据CAP原则:分布式系统不能同时支持 C(一致性)、A(可用性)、P(分区容错性)需要根据自己的实际业务需求选择CAP的其中两个。

常见注册中心
Eureka

服务注册与服务发现简介

Eureka Server 集群当中的每个节点都是通过 Replicate(即复制)来同步数据,没有主节点和从节点之分,所有节点都是平等而且数据都保持一致。因为结点之间是通过异步方式进行同步数据,不保证强一致性,保证可用性,所以是 AP。

注册与发现的工作流程
假设 Eureka Server 已经启动,Eureka Client(服务提供者)启动时把服务注册到 Eureka Server;
Eureka Client(服务提供者)每 30 秒(默认可配置)向 Eureka Sever 发 http 请求(即心跳),即服务续约;
Eureka Server90 秒没有收到向 Eureka Client(服务提供者)的心跳请求,则统计 15 分钟内是否存在 85% 的 Eureka Client(服务提供者)没有发心跳,如果是则进行自我保护状态(比如网络不稳定),如果不是则剔除该 Eureka Client(服务提供者)实例;
Eureka Client(服务消费者)定时调用 Eureka Server 接口获取服务列表更新本地缓存;
Eureka Client(服务消费者)远程调用服务时,先从本地缓存找,如果找到则直接发起服务调用,如果没有则到 Eureka Server 获取服务列表缓存到本地后再发起服务调用;
Eureka Client(服务提供者)应用关闭时会发 HTTP 请求到 Eureka Server,服务端接受请求后把该实例剔除。
Zookeeper

服务注册与服务发现简介

Zookeeper 支持 CP,当集群中如果有节点宕机则需要选举 leader(FastLeaderElection),选举过程需要 30 至 120 秒,选举过程时集群不可用,牺牲时间来保证数据一致性。

注册与发现的工作流程
假设 ZK 已经启动,服务提供者启动时把服务注册到 ZK 注册中心;
ZK 注册中心和服务提供者之间建立一个 Socket 长连接,ZK 注册中心定时向每个服务提供者发数据包,如果服务提供者没响应,则剔除该服务提供者实例,把更新后的服务列表发送给所有服务消费者(即通知);
服务消费者启动时到 ZK 注册中心获取一份服务列表缓存到本地供以后使用;
服务消费者远程调用服务时,先从本地缓存找,如果找到则直接发起服务调用,如果没有则到 ZK 注册中心获取服务列表缓存到本地后再发起服务调用;
当其中一个服务提供者宕机或正常关闭时,ZK 注册中心会把该节点剔除,并通知所有服务消费者更新本地缓存;
当这个服务提供者正常启动后,ZK 注册中心也能感知到,并通知所有服务消费者更新本地缓存。
Nacos

服务注册与服务发现简介

注册与发现的工作流程

服务注册与服务发现简介

假设 Nacos Server 已经启动,服务提供者启动时把服务注册到 Nacos 注册中心;
服务提供者注册成功后,定时发 http 请求(即心跳)到注册中心,证明自身服务实例可用;
如果注册中心长时间没有收到服务提供者的心跳请求,则剔除该实例;
服务消费者发现服务支持两种方式,一种是主动请求注册中心获取服务列表(不推荐),一种是订阅注册中心的服务并提交一个 Listener,如果注册中心的服务有变更,由 Listener 来通知服务消费者更新本地服务列表;
服务消费者获取服务相关信息进行远程调用。
Eureka 不能支撑大量服务实例,因为 Eureka 的每个节点数据都一致,会产生大量的心跳检查等等导致并发性能低下,ZooKeeper 的频繁上下线通知会导致性能下降,而 Nacos 可以支持大量服务实例又不丢性能,据说服务数量能达到 10 万以上。