RPC实现原理之核心技术-注册发现

  1. 服务注册发现的作用

    在高可用的生产环境中,一般都以集群方式提供服务,集群里面的IP可能随时变化,也可能会随着维护扩充或减少节点,客户端需要能够及时感知服务端的变化,获取集群最新服务节点的连接信息。

  2. 服务注册发现功能
    file

    1)服务注册:在服务提供方启动的时候,将对外暴露的接口注册到注册中心内,注册中心将这个服务节点的 IP 和接口等连接信息保存下来。为了检测服务的服务端的有效状态,一般会建立双向心跳机制。

    2)服务订阅:在服务调用方启动的时候,客户端去注册中心查找并订阅服务提供方的 IP,然后缓存到本地,并用于后续的远程调用。如果注册中心信息发生变化, 一般会采用推送的方式做更新。

  3. 服务注册发现的具体流程

    主流服务注册工具有Nacos、Consul、Zookeeper等,
    基于 ZooKeeper 的服务发现:
    ZooKeeper 集群作为注册中心集群,服务注册的时候只需要服务节点向 ZooKeeper 节点写入注册信息即可,利用 ZooKeeper 的 Watcher 机制完成服务订阅与服务下发功能。
    file
    A). 先在 ZooKeeper 中创建一个服务根路径,可以根据接口名命名(例如:/micro/service/com.itcast.xxService),在这个路径再创建服务提供方与调用方目录(server、client),分别用来存储服务提供方和调用方的节点信息。

    B). 服务端发起注册时,会在服务提供方目录中创建一个临时节点,节点中存储注册信 息,比如IP,端口,服务名称等等。

    C). 客户端发起订阅时,会在服务调用方目录中创建一个临时节点,节点中存储调用方的信息,同时watch 服务提供方的目录(/service/com.demo.xxService/server)中所有的服务节点数据。当服务端产生变化时,比如下线或宕机等,ZooKeeper 就会通知给订阅的客户端。

  4. ZooKeeper方案的特点

    ZooKeeper 的一大特点就是强一致性,ZooKeeper 集群的每个节点的数据每次发生更新操作,都会通知其它 ZooKeeper 节点同时执行更新。它要求保证每个节点的数据能够实时的完全一致,这样也就会导致ZooKeeper 集群性能上的下降,ZK是采用CP模式(保证强一致性),如果要注重性能, 可以考虑采用AP模式(保证最终一致)的注册中心组件, 比如Nacos等。

  5. Dubbo注册发现源码剖析

    1. 注册发现的客户端源码(Dubbo Spring Cloud):
      代码处理流程:
      file
      核心源码:
      RegistryProtocol的doRefer方法:

      private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
          RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
          directory.setRegistry(registry);
          directory.setProtocol(protocol);
          // all attributes of REFER_KEY
          Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
          URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
          if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
              directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
              registry.register(directory.getRegisteredConsumerUrl());
          }
          directory.buildRouterChain(subscribeUrl);
          directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                  PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
      
          Invoker invoker = cluster.join(directory);
          ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
          return invoker;
      }
      
      
    2. 注册发现的服务端源码(Dubbo Spring Cloud):
      代码处理流程:
      file
      核心源码:
      RegistryProtocol的export方法:

       public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
          // 获取注册信息
      	URL registryUrl = getRegistryUrl(originInvoker);
          // 获取服务提供方信息
         URL providerUrl = getProviderUrl(originInvoker);
       
          // Subscribe the override data
          // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
          //  the same service. Because the subscribed is cached key with the name of the service, it causes the
          //  subscription information to cover.
          final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
          final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
         overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
       
          providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
          //export invoker
         final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
       
          // 获取订阅注册器
          final Registry registry = getRegistry(originInvoker);
          final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
          ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
                  registryUrl, registeredProviderUrl);
          //to judge if we need to delay publish
          boolean register = registeredProviderUrl.getParameter("register", true);
          if (register) {
              // 进入服务端信息注册处理
              register(registryUrl, registeredProviderUrl);
              providerInvokerWrapper.setReg(true);
         }
       
          // Deprecated! Subscribe to override rules in 2.6.x or before.
      	// 服务端信息订阅处理
         registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
       
          exporter.setRegisterUrl(registeredProviderUrl);
          exporter.setSubscribeUrl(overrideSubscribeUrl);
          //Ensure that a new exporter instance is returned every time export
          return new DestroyableExporter<>(exporter);
      }
      

本文由mirson创作分享,如需进一步交流,请加QQ群:19310171或访问www.softart.cn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦神-mirson

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值