文章目录

  • 为什么有服务发现
  • 服务发现方案
  • DNS
  • ZooKeeper
  • 消息总线的最终一致性注册中心


为什么有服务发现

高可用场景下,一般服务都是以集群模式对外提供服务,集群里的IP随时变化,调用方这个时候就需要一个服务方的服务节点的列表了。

服务发现分为两个步骤:

  • 服务注册: 服务提供方启动的时候,将对外暴露的接口注册到注册中心之中,注册中心将这个服务节点的IP和端口保存下来。
  • 服务订阅:服务调用方启动的时候,去注册中心查找并订阅服务提供方的IP,然后缓存到本地,并用于后续的远程调用。

服务发现方案

DNS

首先看下DNS的查询流程,如下:

如何测试RPC服务 rpc怎么实现服务发现_提供方


通过DNS拿到一个服务者提供的IP,建立长链接,存在什么问题呢?

  • IP端口下线了,服务调用者无法及时的摘除服务节点
  • 扩容的时候,新上线的节点无法快速收到流程

造成上面问题的原因在于DNS的多级缓存机制。

改进版:加一个负载均衡设备

如何测试RPC服务 rpc怎么实现服务发现_如何测试RPC服务_02


不太合适的原因如下:

  • 搭建负载均衡设备或 TCP/IP 四层代理,需求额外成本;
  • 请求流量都经过负载均衡设备,多经过一次网络传输,会额外浪费些性能;
  • 负载均衡添加节点和摘除节点,一般都要手动添加,当大批量扩容和下线时,会有大量
    的人工操作和生效延迟;
  • 我们在服务治理的时候,需要更灵活的负载均衡策略,目前的负载均衡设备的算法还满
    足不了灵活的需求。
ZooKeeper

搭建一个ZooKeeper集群作为注册中心集群,服务注册的时候只需要服务节点向ZooKeeper节点写入注册信息即可,利用ZooKeeper的Watch机制完成服务订阅与服务下发功能。

如何测试RPC服务 rpc怎么实现服务发现_服务调用_03

  • 服务平台管理端先在 ZooKeeper 中创建一个服务根路径,可以根据接口名命名(例 如:/service/com.demo.xxService),在这个路径再创建服务提供方目录与服务调用 方目录(例如:provider、consumer),分别用来存储服务提供方的节点信息和服务调 用方的节点信息。
  • 当服务提供方发起注册时,会在服务提供方目录中创建一个临时节点,节点中存储该服 务提供方的注册信息。
  • 当服务调用方发起订阅时,则在服务调用方目录中创建一个临时节点,节点中存储该服 务调用方的信息,同时服务调用方 watch 该服务的服务提供方目录 (/service/com.demo.xxService/provider)中所有的服务节点数据。
  • 当服务提供方目录下有节点数据发生变更时,ZooKeeper 就会通知给发起订阅的服务调 用方。

存在问题:
当连接到 ZooKeeper 的节点数量特别多,对 ZooKeeper 读写特别频繁,且 ZooKeeper 存储的目 录达到一定数量的时候,ZooKeeper 将不再稳定,CPU 持续升高,最终会产生宕机。

消息总线的最终一致性注册中心

注册数据可以全量缓存在每个注册

中心内存中,通过消息总线来同步数据。当有一个注册中心节点接收到服务节点注册时,会

产生一个消息推送给消息总线,再通过消息总线通知给其它注册中心节点更新数据并进行服

务下发,从而达到注册中心间数据最终一致性

如何测试RPC服务 rpc怎么实现服务发现_如何测试RPC服务_04

  • 当有服务上线,注册中心节点收到注册请求,服务列表数据发生变化,会生成一个消
    息,推送给消息总线,每个消息都有整体递增的版本。
  • 消息总线会主动推送消息到各个注册中心,同时注册中心也会定时拉取消息。对于获取
    到消息的在消息回放模块里面回放,只接受大于本地版本号的消息,小于本地版本号的
    消息直接丢弃,从而实现最终一致性。
  • 消费者订阅可以从注册中心内存拿到指定接口的全部服务实例,并缓存到消费者的内存
    里面。
  • 采用推拉模式,消费者可以及时地拿到服务实例增量变化情况,并和内存中的缓存数据
    进行合并。

为了性能,这里采用了两级缓存,注册中心和消费者的内存缓存,通过异步推拉模式来确保
最终一致性。

目标节点存在已经下 线或不提供指定接口服务的情况,我们放到了 RPC 框架里 面去处理,在服务调用方发送请求到目标节点后,目标节点会进行合法性验证,如果指定接 口服务不存在或正在下线,则会拒绝该请求。服务调用方收到拒绝异常后,会安全重试到其 它节点。

通过可以牺牲掉 CP(强制一致性),而选择 AP(最终一致)