dubbo源码分析第八篇一服务引用内核剖析

服务引用原理图一

  • referencebean 负责服务暴露入口,相关初始化工作,injvm引用和远程引用协议调用
  • 注册协议主要负责注册中心获取以及注册消费者,订阅路由,配置和提供者,通过集群装饰RegistryDirectory统一暴露Invoker ,通过代理构建接口
  • RegistryDirectory既负责提供负载路由功能,也负责监听zk,调用dubboProtocol构建dubboInvoker
  • dubboProtocol也负责通信层的构建
    在这里插入图片描述

服务引用原理图二

在这里插入图片描述

源码分析

ReferenceBean获取消费者代理对象

  • spring的factoryBean,在被依赖注入时,通过getObject获取目标对象
  • 目标对象是一个消费者动态代理对象
  • 构建url参数map
  • 创建代理对象
调用get方法
 public class ReferenceBean<T> implements FactoryBean{
  	@Override
    public Object getObject() {
        return get();
    }
 }
调用init方法
public synchronized T get() {
    checkAndUpdateSubConfigs();

    if (destroyed) {
        throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
    }
    if (ref == null) {
        init();
    }
    return ref;
}

 public synchronized void init() {
  
    step-1: 构建消费者url的key-val对应map
    Map<String, String> map = new HashMap<String, String>();
    map.put(INTERFACE_KEY, interfaceName);
    
    step-2: 构建完毕后map 创建代理
    ref = createProxy(map);

    发布相关bean实例化事件
    dispatch(new ReferenceConfigInitializedEvent(this, invoker));
}
 

createProxy 创建消费者接口动态代理

  • 本地引用匹配 则构建本地Invoker
  • 如果url属性不为空,则根据直连地址构建url
  • 否则查找注册中心构建url
  • 根据url调用相应协议构建Invoker
  • 多url则需要在通过StaticDirectory和RegistryAwareCluster统一Invoker暴露
  • 通过代理工厂创建目标消费者接口代理对象
private T createProxy(Map<String, String> map) {
    step-1: 构建injvmInvoker 判断规则是去InjvmProtocol协议中判断已经暴露的服务servicekey是否匹配消费者 匹配则找到
    if (shouldJvmRefer(map)) {
        URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
        step-2: 直接调用InjvmProtocol返回 Invoker对象
        invoker = REF_PROTOCOL.refer(interfaceClass, url);
       
    } else {
        step-3.1 直连url或者注册url构建urls
        直连地址,可以是服务提供者的地址
        if (url != null && url.length() > 0) { 
            按照,分割
            String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
            if (us != null && us.length > 0) {
                for (String u : us) {
                    URL url = URL.valueOf(u);
                    if (StringUtils.isEmpty(url.getPath())) {
                        url = url.setPath(interfaceName);
                    }
                    注册中心的地址,带上服务引用的配置参数
                    if (UrlUtils.isRegistry(url)) {
                        注册中心的地址,带上服务引用的配置参数
                        urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                    } else {
                        服务提供者的地址
                        urls.add(ClusterUtils.mergeUrl(url, map));
                    }
                }
            }
        } else {
            step-3.2注册url构建urls 获取配置的注册中心地址 一般注册中心走这个
            if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
                checkRegistry();
                这里是获取应用配置的注册信息 RegisterConfig url 也就是注册中心地址
                List<URL> us = ConfigValidationUtils.loadRegistries(this, false);
                if (CollectionUtils.isNotEmpty(us)) {
                    for (URL u : us) {
                        来自于多注册中心ConfigValidationUtils.loadRegistries
                        urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }
            }
        }
        if (urls.size() == 1) {
            step-4: 远程协议调用RegistryProtocol的refer构建Invoker实例
            invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
        } else {
            ...... 删除多注册中心 或者多直连地址
        }
    }

    step-5:创建代理 同服务暴露 创建wrapper对象加速调用 避免反射
    return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}


RegistryProtocol.doRefer第一部分

  • 支持分组功能
 public class RegistryProtocol implements Protocol {
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        url = URLBuilder.from(url)
                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
                .removeParameter(REGISTRY_KEY)
                .build();
        
        支持分组功能
        // group="a,b" or group="*"
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
        String group = qs.get(GROUP_KEY);
        if (group != null && group.length() > 0) {
            if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }
        调用doRefer实现服务引用
        return doRefer(cluster, registry, type, url);
    }
}

RegistryProtocol.doRefer核心

  • 创建RegistryDirectory 负责监听zk,[监听时触发dubboProtocol协议调用]
  • 向zk服务器创建消费者url
  • 向zk服务器订阅providers configurators routers目录
  • RegistryDirectory会通过监听机制触发对dubboProtocol协议调用
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
    ...... 流程中删除部分代码
    step-1:创建 RegistryDirectory 对象,并设置注册中心  RegistryDirectory自身也实现了NotifyListener监听zk
    RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
    step-2:向注册中心注册自己(服务消费者)
    if (directory.isShouldRegister()) {
        directory.setRegisteredConsumerUrl(subscribeUrl);
        registry.register(directory.getRegisteredConsumerUrl());
    }
    directory.buildRouterChain(subscribeUrl);
    step-3: 订阅zk的providers configurators router目录变更;从而触发dubbo协议构建Invoker到directory
    directory.subscribe(toSubscribeUrl(subscribeUrl));
    step-3:统一暴露集群Invoker 默认failover集群
    Invoker<T> invoker = cluster.join(directory);
    List<RegistryProtocolListener> listeners = findRegistryProtocolListeners(url);
    if (CollectionUtils.isEmpty(listeners)) {
        return invoker;
    }
    // 路由 集群
    RegistryInvokerWrapper<T> registryInvokerWrapper = new RegistryInvokerWrapper<>(directory, cluster, invoker);
    
    return registryInvokerWrapper;
}

RegistryDirectory.subscribe

  • 将RegistryDirectory加入注册中心集合,用于将来监听时查询指定监听器负责监听事件
  • doSubscribe实现监听订阅逻辑
 public void subscribe(URL url) {
        setConsumerUrl(url);
        CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this);
        serviceConfigurationListener = new ReferenceConfigurationListener(this, url);
        将自己作用NotifyListener监听注册中心
        registry.subscribe(url, this);
    }RegistryDirectory添加到注册中心subscribed集合
 public void subscribe(URL url, NotifyListener listener) {
	   ...... 删除其他代码
	   Set<NotifyListener> listeners = subscribed.computeIfAbsent(url, n -> new ConcurrentHashSet<>());
	   listeners.add(listener);
}


public void subscribe(URL url, NotifyListener listener) {
    将监听器加入注册中心元数据
    super.subscribe(url, listener);
    removeFailedSubscribed(url, listener);
    try {
        实现订阅
        doSubscribe(url, listener);
    } catch (Exception e) {
        ...... 删除其他代码
    }
}

ZookeeperRegistry.doSubscribe

  • 根据分类Categories监听订阅相关目录
  • 通过notify实现订阅事件处理
public void doSubscribe(final URL url, final NotifyListener listener) {
    List<URL> urls = new ArrayList<>();
    根据toCategoriesPath订阅配置 路由 提供者目录
    for (String path : toCategoriesPath(url)) {
        ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.computeIfAbsent(url, k -> {
            return new ConcurrentHashMap<>();
        });
        step-1: 向zk注册监听订阅
        zkClient.create(path, false);
        List<String> children = zkClient.addChildListener(path, zkListener);
        if (children != null) {
            urls.addAll(toUrlsWithEmpty(url, path, children));
        }
    }
    step-2: 首次全量数据获取完成时,调用 `#notify(...)` 方法,回调 NotifyListener
    notify(url, listener, urls);
   
}

ZookeeperRegistry.notify

  • 通过zk变更调用RegistryDirectory.notify
protected void notify(URL url, NotifyListener listener, List<URL> urls) {
    for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
        这里会调用RegisterDirectory进行refer跟新消费端的Invoker信息 调用dubboProtocol.refer [当然还有其他监听listener,这里我们只关注消费端]
        listener.notify(categoryList);
      
    }
}

RegistryDirectory.notify通知核心

  • 刷新配置信息
  • 刷新路由信息
  • 刷新overrideDirectoryUrl和 提供者信息
 public synchronized void notify(List<URL> urls) {
    Map<String, List<URL>> categoryUrls = urls.stream()
            .filter(Objects::nonNull)
            .filter(this::isValidCategory)
            .filter(this::isNotCompatibleFor26x)
            .collect(Collectors.groupingBy(this::judgeCategory));
    step-1: 刷新配置信息
    List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
    this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);
    step-2: 刷新路由信息
    List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
    toRouters(routerURLs).ifPresent(this::addRouters);
    // providers
    List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
    ...... 删除其他代码
    step-3: 刷新overrideDirectoryUrl和 提供者信息
    refreshOverrideAndInvoker(providerURLs);
}

RegistryDirectory.refreshInvoker构建DubboInvoker

  • 根据zk的监听变化调用dubboProtocol.refer 构建 新的Invokers
  • 变更路由链
  • 修改RegistryDirectory的urlInvokerMap
  private void refreshInvoker(List<URL> invokerUrls) {
    ...... 删除其他代码
    step-1: 根据zk的监听变化调用dubboProtocol.refer 构建 新的Invokers
    Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);

    List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
    step-2: 变更路由链 
    routerChain.setInvokers(newInvokers);
    this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
    step-3: 修改RegistryDirectory的urlInvokerMap
    this.urlInvokerMap = newUrlInvokerMap;
    ...... 删除其他代码
           
}

toInvokers 调用dubboProtocol构建Invoker

private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
    根据disable enable判断允许变更否
    boolean enabled = true;
    if (url.hasParameter(DISABLED_KEY)) {
        enabled = !url.getParameter(DISABLED_KEY, false);
    } else {
        enabled = url.getParameter(ENABLED_KEY, true);
    }
    if (enabled) {
        使用dubbo协议创建具有远程通信功能的Invoker
        构建dubbo invoker  dubboprotocol  其中dubboInvoker含有 数据转换ExchangeClient 以及通信层对象
        invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
    }
    
    if (invoker != null) { // Put new invoker in cache
        newUrlInvokerMap.put(key, invoker);
    }
    keys.clear();
    return newUrlInvokerMap;
}

dubbo协议refer实现

  • 创建具有通信功能的dubboInvoker
   public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url);

        // create rpc invoker.
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);

        return invoker;
    }

总结

  • 讲述服务引用核心流程构建Invoker ,Invoker加目标接口构建动态代理对象
  • Invoker的核心是RegistryDirectory
  • RegistryDirectory负责监听zk,处理提供者,配置路由的变更
  • RegistryDirectory负责在服务调用时处理路由负载均衡
  • dubboInvoker 负责处理通信层的构建
  • RegistryProtocol主要负责注册消费者URL和监听服务提供者,路由以及配置目录URL
  • 监听的事件通过RegistryDirectory调用DubboProtocol构建dubboInvoker

扩展点一服务引用Invoker结构

ClusterInvokerRegistryDirectoryInvokerDelegateListenerInvokerWrapperCallbackRegistrationInvokerAsyncToSyncInvokerDubboInvokerExchangeClient
统一暴露,容错熔断负载路由委派监听过滤异步转同步协议Invoker通信层
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值