dubbo服务消费过程(二)

服务的订阅和通知

Directory

在这里插入图片描述

Directory 继承自 Node 接口,Node 这个接口继承者比较多,像 Registry、Monitor、Invoker 等均继承了这个接口。这个接口包含了一个获取配置信息的方法 getUrl,实现该接口的类可以向外提供配置信息。另外,大家注意看 RegistryDirectory 实现了 NotifyListener 接口,当注册中心节点信息发生变化后,RegistryDirectory 可以通过此接口方法得到变更信息,并根据变更信息动态调整内部 Invoker 列表

在容错过程中(如FailoverClusterInvoker)会使用Directory#list来获取所有的invoke的列表。
模板模式

  • Directory顶层接口
  • AbstractDirectory封装了通用实现逻辑:list等
  • StaticDirectory Directory的静态列表实现,提供静态的Invoke列表,将传入的Invoker列表封装成静态的Directory对象,里面的列表不会改变,在ReferenceConfig#createProxy使用CLUSTER.join(new StaticDirectory(invokers))
  • RegistryDirectoryDirectory的动态列表实现提供动态的Invoke列表。会自动从注册中心更新Invoker列表,配置信息、路由信息。

RegistryDirectory
RegistryDirectory实现两点:
(1) 框架与注册中心的订阅,并动态更新本地Invoker列表、路由列表、配置信息的逻辑
(2) 实现父类的doList方法

订阅与动态更新主要方法subscribe、notify、refreshInvoker,辅助方法toConfigurators、toRouter

服务的订阅subscribe

注册中心ZK 的节点订阅和通知
调用链
RegistryProtocol#refer
-> RegistryProtocol#doRefer
-> directory#subscribe
-> registry#subscribe -->这个registry 是ZookeeperRegistry

ZookeeperRegistry父类FailbackRegistry#subscribe ->ZookeeperRegistry.doSubscribe()

会去监听下面的节点的路径的子节点变动

/dubbo/org.apache.dubbo.demo.DemoService/providers
/dubbo/org.apache.dubbo.demo.DemoService/configurators
/dubbo/org.apache.dubbo.de mo.DemoService/routers

directory#subscribe

订阅某个URL的更新信息。Dubbo在引用每个需要RPC调用(refer)Bean的时候,会调用directory.subscribe订阅这个bean的各种URL变化

public void subscribe(URL url) {
    
	  // 设置consumerUrl 
      setConsumerUrl(url);
      // 把当前的RegistryDriectory作为listener,去监听zk节点的变化
      CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this);
      serviceConfigurationListener = new ReferenceConfigurationListener(this, url);
      registry.subscribe(url, this);
  }

FailbackRegistry#subscribe

移除失效的listener,调用doSubscribe进行订阅

// listener为RegistryDirectory
public void subscribe(URL url, NotifyListener listener) {
   
    super.subscribe(url, listener);
    // 移除失效的listener
    removeFailedSubscribed(url, listener); 
    try {
   
        // 调用ZookeeperRegistry.doSubscribe 实现 
        doSubscribe(url, listener);
    } catch (Exception e) {
   
        Throwable t = e;

        List<URL> urls = getCacheUrls(url);
        if (CollectionUtils.isNotEmpty(urls)) {
   
            notify(url, listener, urls);
            logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
        } else {
   
            // If the startup detection is opened, the Exception is thrown directly.
            boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
                    && url.getParameter(Constants.CHECK_KEY, true);
            boolean skipFailback = t instanceof SkipFailbackWrapperException;
            if (check || skipFailback) {
   
                if (skipFailback) {
   
                    t = t.getCause();
                }
                throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
            } else {
   
                logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
            }
        }
        // Record a failed registration request to a failed list, retry regularly
        addFailedSubscribed(url, listener); // 失败重试
    }
}

ZookeeperRegistry#doSubscribe

主要实现把所有Service层发起的订阅以及指定的Service层发起的订阅分开处理。

  • 所有Service层类似于监控中心发起的订阅。
  • 指定的Service层发起的订阅可以看作是服务消费者的订阅。
    @Override
    public void doSubscribe(final URL url, final NotifyListener listener) {
   
        try {
   
            if (ANY_VALUE.equals(url.getServiceInterface())) {
    // 这里主要用与服务端和zk连接 注册服务
               ... // 省略部分代码
            } else {
    // 消费端和zk连接 订阅节点 
                List<URL> urls = new ArrayList<>();
                for (String path : toCategoriesPath(url)) {
   
                    ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                    if (listeners == null) {
   
                       // 如果之前该路径没有添加过listener,则创建一个map来放置listener
                        zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
                        listeners = zkListeners.get(url);
                    }
                    ChildListener zkListener = listeners.get(listener);
                    if (zkListener == null) {
   
                       // 如果没有添加过对于子节点的listener,则创建,通知服务变化 回调NotifyListener
                        listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
                        zkListener = listeners.get(listener);
                    }
                    zkClient.create(path, false);
                    //添加path节点的当前节点及子节点监听,并且获取子节点信息 
                    //也就是dubbo://ip:port/...
                    List<String> children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
   
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
                // 调用notify进行通知,对已经可用的列表进行通知
                notify(url, listener, urls);
            }
        } catch (Throwable e) {
   
            throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }

服务通知notify

FailbackRegistry.notify

调用FailbackRegistry.notify, 对参数进行判断。 然后调用AbstractRegistry.notify方法

 protected void notify(URL url, NotifyListener listener, List<URL> urls) {
   
        if (url == null) {
   
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
   
            throw new IllegalArgumentException("notify listener == null");
        }
        try {
   
            doNotify(url, listener, urls);
        } catch (Exception t) {
   
            // Record a failed registration request to a failed list, retry regularly
            addFailedNotified(url, listener, urls);
            logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
        }
    }

 protected void doNotify(URL url, NotifyListener listener, List<URL> urls) {
   
     super.notify(url, listener, urls);
 }

AbstractRegistry.notify

protected void notify(URL url, NotifyListener listener, List<URL> urls) {
   
      if (url == null) {
   
          throw new IllegalArgumentException("notify url == null");
      }
      if (listener == null) {
   
          throw new IllegalArgumentException("notify listener == null");
      }
      if ((CollectionUtils.isEmpty(urls))
              && !ANY_VALUE.equals(url.getServiceInterface())) {
   
          logger.warn("Ignore empty notify urls for subscribe url " + url);
          return;
      }
      if (logger.isInfoEnabled()) {
   
          logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
      }
      // keep every provider's category.
      Map<String
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和Spring 框架无缝集成。Dubbo主要核心部件Remoting:网络通信框架,实现了sync-over-async和request-response消息机制 RPC:一个远程过程调用的抽象,支持负载均衡、容灾和集群功能 Registry:服务目录框架用于服务的注册和服务事件发布和订阅Dubbo功能特点(1) 连通性: 注册中心负责服务地址的注册与查找,相当于目录服务服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表 注册中心和监控中心都是可选的,服务消费者可以直连服务提供者 (2) 健状性: 监控中心宕掉不影响使用,只是丢失部分采样数据 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务 注册中心对等集群,任意一台宕掉后,将自动切换到另一台 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯 服务提供者无状态,任意一台宕掉后,不影响使用 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复 (3) 伸缩性: 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费
Dubbo 是一种分布式服务框架,它支持高性能的远程服务调用和服务治理。下面是 Dubbo 服务的暴露过程: 1. 配置服务接口:首先,你需要定义一个服务接口,它描述了你希望暴露的服务的方法和参数。 2. 实现服务接口:然后,你需要编写一个类来实现这个服务接口,提供具体的业务逻辑实现。 3. 配置服务实现:在 Dubbo 的配置文件中,你需要配置服务的实现类,指定要暴露的接口和端口等信息。 4. 启动 Dubbo 服务:在应用启动时,Dubbo 框架会根据配置文件初始化相关的组件,并启动服务提供者。 5. 注册服务Dubbo 服务提供者会将自己的地址、接口和方法等信息注册到注册中心,以便消费者能够发现和调用该服务。 6. 监听请求:一旦 Dubbo 服务提供者启动成功并注册到注册中心,它就会开始监听来自消费者的远程调用请求。 7. 处理请求:当有消费者发起远程调用请求时,Dubbo 服务提供者会根据请求的接口和方法等信息,找到对应的实现类并执行相应的业务逻辑。 8. 返回结果:Dubbo 服务提供者执行完业务逻辑后,会将结果返回给消费者,完成一次远程调用。 总结来说,Dubbo 服务的暴露过程包括定义服务接口、实现服务接口、配置服务实现、启动 Dubbo 服务、注册服务、监听请求、处理请求和返回结果等步骤。通过这些步骤,Dubbo 实现了远程服务的调用和治理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值