微服务系列-注册和发现服务
注册中心原理
在微服务架构下,主要有三种角色:服务提供者(RPC Server)、服务消费者(RPC Client)和服务注册中心(Registry),三者的交互关系请看下面这张图。
zookeeper原理图
zookeeper目录
注册中心如何存储服务信息
服务一般会分成多个不同的分组,每个分组的目的不同。一般来说有下面几种分组方式。
1.核心与非核心,从业务的核心程度来分。
2.机房,从机房的维度来分。
3.线上环境与测试环境,从业务场景维度来区分。
所以注册中心存储的服务信息一般包含三部分内容:分组、服务名以及节点信息,节点信息又包括节点地址和节点其他信息。从注册中心中获取的信息结构大致如下图所示。
具体存储的时候,一般是按照“服务 - 分组 - 节点信息”三层结构来存储,可以用下图来描述。Service 代表服务的具体分组,Cluster 代表服务的接口名,节点信息用 KV 存储。
注册中心具体是如何工作的,包括四个流程。
1.服务提供者注册流程。
2.服务提供者反注册流程。
3.服务消费者查询流程。
4.服务消费者订阅变更流程。
注册中心是如何工作的
1. 如何注册节点
服务注册流程主要有下面几个步骤:
~首先查看要注册的节点是否在白名单内?如果不在就抛出异常,在的话继续下一步。
~其次要查看注册的 Cluster(服务的接口名)是否存在?如果不存在就抛出异常,存在的话继续下一步。
~然后要检查 Service(服务的分组)是否存在?如果不存在则抛出异常,存在的话继续下一步。
~最后将节点信息添加到对应的 Service 和 Cluster 下面的存储中。
2. 如何反注册
服务提供者节点反注册的流程
节点反注册流程主要包含下面几个步骤:
~查看 Service(服务的分组)是否存在,不存在就抛出异常,存在就继续下一步。
~查看 Cluster(服务的接口名)是否存在,不存在就抛出异常,存在就继续下一步。
~删除存储中 Service 和 Cluster 下对应的节点信息。
~更新 Cluster 的 sign 值。
3. 如何查询节点信息
服务消费者是如何从注册中心查询服务提供者的节点信息
服务消费者查询节点信息主要分为下面几个步骤:
~首先从 localcache(本机内存)中查找,如果没有就继续下一步。这里为什么服务消费者要把服务信息存在本机内存呢?主要是因为服务节点信息并不总是时刻变化的,并不需要每一次服务调用都要调用注册中心获取最新的节点信息,只需要在本机内存中保留最新的服务提供者的节点列表就可以。
~接着从 snapshot(本地快照)中查找,如果没有就继续下一步。这里为什么服务消费者要在本地磁盘存储一份服务提供者的节点信息的快照呢?这是因为服务消费者同注册中心之间的网络不一定总是可靠的,服务消费者重启时,本机内存中还不存在服务提供者的节点信息,如果此时调用注册中心失败,那么服务消费者就拿不到服务节点信息了,也就没法调用了。本地快照就是为了防止这种情况的发生,即使服务消费者重启后请求注册中心失败,依然可以读取本地快照,获取到服务节点信息。
4. 如何订阅服务变更
服务消费者如何订阅服务提供者的变更信息呢?
主要分为下面几个步骤:
~服务消费者从注册中心获取了服务的信息后,就订阅了服务的变化,会在本地保留 Cluster 的 sign 值。
~服务消费者每隔一段时间,调用 getSign() 函数,从注册中心获取服务端该 Cluster 的 sign 值,并与本地保留的 sign 值做对比,如果不一致,就从服务端拉取新的节点信息,并更新 localcache 和 snapshot。
以上就是服务注册和反注册、服务查询和服务订阅变更的基本流程。除此之外,实现服务注册与发现时遇到的几个问题
开源注册中心选择
当下主流的服务注册与发现的解决方案,主要有两种:
~应用内注册与发现:注册中心提供服务端和客户端的 SDK,业务应用通过引入注册中心提供的 SDK,通过 SDK 与注册中心交互,来实现服务的注册和发现。
~应用外注册与发现:业务应用本身不需要通过 SDK 与注册中心打交道,而是通过其他方式与注册中心交互,间接完成服务注册与发现。
1. 应用内
采用应用内注册与发现的方式,最典型的案例要属 Netflix 开源的 Eureka,官方架构图如下。
对着这张图,介绍下 Eureka 的架构,它主要由三个重要的组件组成:
~Eureka Server:注册中心的服务端,实现了服务信息注册、存储以及查询等功能。
~服务端的 Eureka Client:集成在服务端的注册中心 SDK,服务提供者通过调用 SDK,实现服务注册、反注册等功能。
~客户端的 Eureka Client:集成在客户端的注册中心 SDK,服务消费者通过调用 SDK,实现服务订阅、服务更新等功能。
2. 应用外
采用应用外方式实现服务注册和发现,最典型的案例是开源注册中心 Consul,它的架构图如下。
通过这张架构图,可以看出来使用 Consul 实现应用外服务注册和发现主要依靠三个重要的组件:
~Consul:注册中心的服务端,实现服务注册信息的存储,并提供注册和发现服务。
~Registrator:一个开源的第三方服务管理器项目,它通过监听服务部署的 Docker 实例是否存活,来负责服务提供者的注册和销毁。
~Consul Template:定时从注册中心服务端获取最新的服务提供者节点列表并刷新 LB 配置(比如 Nginx 的 upstream),这样服务消费者就通过访问 Nginx 就可以获取最新的服务提供者信息。
这两种解决方案的不同之处在于应用场景,应用内的解决方案一般适用于服务提供者和服务消费者同属于一个技术体系;应用外的解决方案一般适合服务提供者和服务消费者采用了不同技术体系的业务场景,比如服务提供者提供的是 C++ 服务,而服务消费者是一个 Java 应用,这时候采用应用外的解决方案就不依赖于具体一个技术体系。同时,对于容器化后的云应用来说,一般不适合采用应用内 SDK 的解决方案,因为这样会侵入业务,而应用外的解决方案正好能够解决这个问题。
需要考虑的问题
1. 高可用性
实现高可用性的方法主要有两种:
~集群部署,顾名思义就是通过部署多个实例组成集群来保证高可用性,这样的话即使有部分机器宕机,将访问迁移到正常的机器上就可以保证服务的正常访问。
~多 IDC 部署,就是部署在不止一个机房,这样能保证即使一个机房因为断电或者光缆被挖断等不可抗力因素不可用时,仍然可以通过把请求迁移到其他机房来保证服务的正常访问。
以 Consul 为例,来看看它是如何通过这两种方法来保证注册中心的高可用性。
从下面的官方架构图中你可以看到,一方面,在每个数据中心(DATACENTER)内都有多个注册中心 Server 节点可供访问;另一方面还可以部署在多个数据中心来保证多机房高可用性。
2. 数据一致性
涉及分布式系统中著名的 CAP 理论,即同时满足一致性、可用性、分区容错性这三者是不可能的,其中 C(Consistency)代表一致性,A(Availability)代表可用性,P(Partition Tolerance)代表分区容错性。
注册中心一般采用分布式集群部署,也面临着 CAP 的问题,根据 CAP 不能同时满足,所以不同的注册中心解决方案选择的方向也就不同,大致可分为两种。
~CP 型注册中心,牺牲可用性来保证数据强一致性,最典型的例子就是 ZooKeeper,etcd,Consul 了。ZooKeeper 集群内只有一个 Leader,而且在 Leader 无法使用的时候通过 Paxos 算法选举出一个新的 Leader。这个 Leader 的目的就是保证写信息的时候只向这个 Leader 写入,Leader 会同步信息到 Followers,这个过程就可以保证数据的强一致性。但如果多个 ZooKeeper 之间网络出现问题,造成出现多个 Leader,发生脑裂的话,注册中心就不可用了。而 etcd 和 Consul 集群内都是通过 raft 协议来保证强一致性,如果出现脑裂的话, 注册中心也不可用。
~AP 型注册中心,牺牲一致性来保证可用性,最典型的例子就是 Eureka 了。对比下 Zookeeper,Eureka 不用选举一个 Leader,每个 Eureka 服务器单独保存服务注册地址,因此有可能出现数据信息不一致的情况。但是当网络出现问题的时候,每台服务器都可以完成独立的服务。
RPC框架选择
限定语言平台的开源 RPC 框架
1. Dubbo
2. Motan
Motan 与 Dubbo 的架构类似,都需要在 Client 端(服务消费者)和 Server 端(服务提供者)引入 SDK,其中 Motan 框架主要包含下面几个功能模块。
~register:用来和注册中心交互,包括注册服务、订阅服务、服务变更通知、服务心跳发送等功能。Server 端会在系统初始化时通过 register 模块注册服务,Client 端会在系统初始化时通过 register 模块订阅到具体提供服务的 Server 列表,当 Server 列表发生变更时也由 register 模块通知 Client。
~protocol:用来进行 RPC 服务的描述和 RPC 服务的配置管理,这一层还可以添加不同功能的 filter 用来完成统计、并发限制等功能。
~serialize:将 RPC 请求中的参数、结果等对象进行序列化与反序列化,即进行对象与字节流的互相转换,默认使用对 Java 更友好的 Hessian 2 进行序列化。
~transport:用来进行远程通信,默认使用 Netty NIO 的 TCP 长链接方式。
~cluster:Client 端使用的模块,cluster 是一组可用的 Server 在逻辑上的封装,包含若干可以提供 RPC 服务的 Server,实际请求时会根据不同的高可用与负载均衡策略选择一个可用的 Server 发起远程调用。
3. Tars
4. Spring Cloud
跨语言平台的开源 RPC 框架
1. gRPC
2. Thrift