设为首页 加入收藏

TOP

Dubbo 中 Zookeeper 注册中心原理分析(四)
2023-07-25 21:43:48 】 浏览:86
Tags:Dubbo Zookeeper
ressURL cachedURL = oldURLs.remove(rawProvider); if (cachedURL == null) { cachedURL = createURL(rawProvider, copyOfConsumer, getExtraParameters()); if (cachedURL == null) { continue; } } newURLs.put(rawProvider, cachedURL); } } // 清除老的缓存数据 evictURLCache(consumer); // 更新stringUrls缓存 stringUrls.put(consumer, newURLs); return new ArrayList<>(newURLs.values()); }

 

protected ServiceAddressURL createURL(String rawProvider, URL consumerURL,
                                      Map<String, String> extraParameters) {
    boolean encoded = true;
    
    int paramStartIdx = rawProvider.indexOf(ENCODED_QUESTION_MARK);
     
    if (paramStartIdx == -1) {
        encoded = false;
    }
    // 解析rawProvider, 一分为二, 一个用来创建URLAddress、一个用来创建URLParam
    String[] parts = URLStrParser.parseRawURLToArrays(rawProvider, paramStartIdx);
    if (parts.length <= 1) {
        return DubboServiceAddressURL.valueOf(rawProvider, consumerURL);
    }
 
    String rawAddress = parts[0];
    String rawParams = parts[1];
    boolean isEncoded = encoded;
     
    // 从缓存stringAddress缓存中获取URLAddress, 不存在则创建
    URLAddress address =
        stringAddress.computeIfAbsent(rawAddress,
                   k -> URLAddress.parse(k, getDefaultURLProtocol(), isEncoded));
    address.setTimestamp(System.currentTimeMillis());
 
    // 从缓存stringParam缓存中获取URLParam, 不存在则创建
    URLParam param = stringParam.computeIfAbsent(rawParams,
                             k -> URLParam.parse(k, isEncoded, extraParameters));
    param.setTimestamp(System.currentTimeMillis());
 
    // 用获取到的URLAddress、URLParam直接new创建ServiceAddressURL
    ServiceAddressURL cachedURL = createServiceURL(address, param, consumerURL);
     
    // 判断创建出来的ServiceAddressURL是否和当前消费者匹配, 不匹配返回null
    if (isMatch(consumerURL, cachedURL)) {
        return cachedURL;
    }
    return null;
}

2.4 ZooKeeperRegistry

2.4.1 注册

根据传入的URL生成该节点要在ZooKeeper服务端上注册的节点路径,值为如下形式:/dubbo/org.apache.dubbo.springboot.demo.DemoService/providers/服务信息,/dubbo是根路径,接下来是服务的接口名,然后/providers是类型信息(如果是消费者则是/consumers节点,路由信息则是/routers),最后是URL的字符串表示。得到节点路径后,会根据URL的dynamic参数(默认是true)决定在ZooKeeper服务端上创建的是临时节点,还是持久节点,默认是临时节点。

注册后数据结构如下图所示。

图片

2.4.2 订阅

订阅的核心是通过ZooKeeperClient在指定的节点的添加ChildListener,当该节点的子节点数据发生变化时,ZooKeeper服务端会通知到该ChildListener的notify方法,然后调用到对应的NotifyListener方法,刷新消费者本地的服务提供者列表等信息。

doSubscribe方法主要分为两个分支:URL的interface参数明确指定了为*(订阅所有,通常实际使用中不会这么使用,Dubbo的控制后台会订阅所有)或者就订阅某个服务接口,接下来分析订阅某个指定服务接口这个分支代码。这块涉及到三个监听器(它们是一对一的):

图片

 

1)NotifyListener,Dubbo中定义的通用的订阅相关的监听器。它是定义在dubbo-registry-api模块中的,不仅仅在ZooKeeper注册中心模块中使用。

2)ChildListener,Dubbo中定义的针对ZooKeeper注册中心的监听器,用来监听指定节点子节点的数据变化。

3)CuratorWatcher,Curator框架中的监听器,Curator是常用的ZooKeeper客户端,如果Dubbo采用其它ZooKeeper客户端工具,这块就是其它相关的监听器逻辑了。

当订阅的服务数据发生变化时,最先会触发到CuratorWatcher的process方法,process方法中会调用ChildListener的childChanged方法,在childChanged方法会继续触发调用到ZooKeeperRegistry的notify方法。这里有两点需要注意:

1)因为doSubscribe方法中通过ZooKeeperClient添加Watcher监听器时,使用的是usingWatcher,这是一次性的,所以在CuratorWatcher的实现CuratorWatcherImpl的process方法中,当收到ZooKeeper的变更数据推送时,会再次在path上注册Watcher。

2)在ChildListener的实现RegistryChildListenerImpl的doNotify方法中,会调用ZooKeeperRegistry的toUrlsWithEmpty将传入的字符串形式的服务提供者列表等数据转换成对应的ServiceAddressURL列表数据,以供后面使用。

明确了三个监听器的含义之后,接下来分析doSubscribe的逻辑就简单了。首先会调用toCategoriesPath方法获取三个path路径,分别是

  • /dubbo/org.apache.dubbo.demo.DemoService/providers

  • /d

首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇SpringBoot学习笔记 - 构建、简化.. 下一篇easy excel 导入导出

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目