dubbo源码之服务引用

在这里插入图片描述

这个是dubbo 调用的流程图,这篇文件主要要介绍第二步中的服务订阅和第四步代理类的封装过程,这次使用的源码为2.7.1

服务引用大致流程

在这里插入图片描述

Provider将自己的服务暴露出来,注册到注册中心, Consumer从注册中心得知 Provider 的信息,然后自己封装一个调用类对Provider 实现调用。这次使用的源码为2.7.1,采用注解方式引入。dubbo入门

服务引用大致流程

通过前面文章可以看到,我们这次是采用注解方式引入。因此在初始化引入对应接口类进行属性赋值的时候,会将服务引入。
在这里插入图片描述

这一部分涉及到spring bean生命周期的内容,故在这个暂时不展开。我们直接从
ReferenceAnnotationBeanPostProcessor 类 的doGetInjectedBean 方法中展开。
在这里插入图片描述

通过调用这一步构建 buildReferencedBeanName 构建出名字依赖的名字
consumers:dubbo:service.ProviderService:1.0.0:dev,作为判断容器是否存在的依据。

ReferenceAnnotationBeanPostProcessor #buildInvocationHandler 方法

private InvocationHandler buildInvocationHandler(String referencedBeanName, ReferenceBean referenceBean) {
//通过引用名字构建hander(这个时候的handler没有调用信息)
        ReferenceAnnotationBeanPostProcessor.ReferenceBeanInvocationHandler handler = (ReferenceAnnotationBeanPostProcessor.ReferenceBeanInvocationHandler)this.localReferenceBeanInvocationHandlerCache.get(referencedBeanName);
        if (handler == null) {
            handler = new ReferenceAnnotationBeanPostProcessor.ReferenceBeanInvocationHandler(referenceBean);
        }
//由于第一次调用,容器中并不存在该引用
        if (this.applicationContext.containsBean(referencedBeanName)) {
            this.localReferenceBeanInvocationHandlerCache.put(referencedBeanName, handler);
        } else {
        //初始该应用
            handler.init();
        }

        return handler;
    }

ReferenceAnnotationBeanPostProcessor 中的内部类ReferenceBeanInvocationHandler的init()方法

this.referenceBean 的类为ReferenceBean ,其get() 方法。是其父类ReferenceConfig 的方法
在这里插入图片描述
这个里面最终调用了ReferenceConfig 中的init方法
在这里插入图片描述

在这里插入图片描述

上面是处理器的调用调用过程,具体invoker的核心逻辑在ReferenceConfig 中init方法中

ReferenceConfig 中init

private void init() {
//如果没有初始化
        if (!this.initialized) {
          。。。(省略部分代码)这部分是构建map
          //构建代理对象
            this.ref = this.createProxy(map);
            String serviceKey = URL.buildKey(this.interfaceName, this.group, this.version);
            ApplicationModel.initConsumerModel(serviceKey, this.buildConsumerModel(serviceKey, attributes));
        }
    }

在这里插入图片描述

ReferenceConfig# createProxy代理对象

private T createProxy(Map<String, String> map) {
        URL metadataReportService;
        URL registryURL;
        //判断是否需要内部引用。通过直接走本地协议的 URL 然后进行服务的引入
        if (this.shouldJvmRefer(map)) {
            metadataReportService = (new URL("injvm", "127.0.0.1", 0, this.interfaceClass.getName())).addParameters(map);
            this.invoker = refprotocol.refer(this.interfaceClass, metadataReportService);
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + this.interfaceClass.getName());
            }
        } else {
            URL u;
            URL url;
            //如果不是本地,那肯定是远程了,接下来就是判断是点对点直连 provider 还是通过注册中心拿到 //provider 信息再连接 provider 了,如果配置了 url 的情况,如果配置了 url 那么不是直连的地址
            //,就是注册中心的地址
            if (this.url != null && this.url.length() > 0) {
                String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(this.url);
                if (us != null && us.length > 0) {
                    String[] var11 = us;
                    int var14 = us.length;

                    for(int var16 = 0; var16 < var14; ++var16) {
                        String u = var11[var16];
                        URL url = URL.valueOf(u);
                        if (StringUtils.isEmpty(url.getPath())) {
                            url = url.setPath(this.interfaceName);
                        }
						//判断是否是注册中心
                        if ("registry".equals(url.getProtocol())) {
                        //如果是注册中心地址将map转化为map查询字符串,并作为refer参数添加到url中
                            this.urls.add(url.addParameterAndEncoded("refer", StringUtils.toQueryString(map)));
                        } else {
                        //如果是点对点会合并url,移除服务提供者一下参数
                            this.urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else {
            //如果没有配置地址,则根据map注册url
                this.checkRegistry();
                //服务暴露同样是通过这个方法加载配置,ture 代表为是服务提供者,false 代表服务消费者
                List<URL> us = this.loadRegistries(false);
                if (CollectionUtils.isNotEmpty(us)) {
                    for(Iterator var3 = us.iterator(); var3.hasNext(); this.urls.add(u.addParameterAndEncoded("refer", StringUtils.toQueryString(map)))) {
                        u = (URL)var3.next();
                        url = this.loadMonitor(u);
                        if (url != null) {
                            map.put("monitor", URL.encode(url.toFullString()));
                        }
                    }
                }

                if (this.urls.isEmpty()) {
                    throw new IllegalStateException("No such any registry to reference " + this.interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                }
            }
//构建的url参数
//registry://192.168.25.128:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-consumer&dubbo=2.0.2&pid=317360&qos.enable=false&refer=application%3Ddubbo-consumer%26default.lazy%3Dfalse%26default.sticky%3Dfalse%26default.timeout%3D30000%26dubbo%3D2.0.2%26group%3Ddev%26interface%3Dservice.ProviderService%26lazy%3Dfalse%26methods%3DgetProviderData%26pid%3D317360%26qos.enable%3Dfalse%26register.ip%3D192.168.1.101%26release%3D2.7.1%26revision%3D1.0.0%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1600589714223%26version%3D1.0.0&registry=zookeeper&release=2.7.1&timeout=4000&timestamp=1600591668151
            if (this.urls.size() == 1) {
                this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList();
                registryURL = null;
                Iterator var15 = this.urls.iterator();

                while(var15.hasNext()) {
                    url = (URL)var15.next();
                    invokers.add(refprotocol.refer(this.interfaceClass, url));
                    if ("registry".equals(url.getProtocol())) {
                        registryURL = url;
                    }
                }

                if (registryURL != null) {
                    u = registryURL.addParameter("cluster", "registryaware");
                    this.invoker = cluster.join(new StaticDirectory(u, invokers));
                } else {
                    this.invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }

        if (this.shouldCheck() && !this.invoker.isAvailable()) {
            this.initialized = false;
            throw new IllegalStateException("Failed to check the status of the service " + this.interfaceName + ". No provider available for the service " + (this.group == null ? "" : this.group + "/") + this.interfaceName + (this.version == null ? "" : ":" + this.version) + " from the url " + this.invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
        } else {
            if (logger.isInfoEnabled()) {
                logger.info("Refer dubbo service " + this.interfaceClass.getName() + " from url " + this.invoker.getUrl());
            }

            metadataReportService = null;
            MetadataReportService metadataReportService;
            if ((metadataReportService = this.getMetadataReportService()) != null) {
                registryURL = new URL("consumer", (String)map.remove("register.ip"), 0, (String)map.get("interface"), map);
                metadataReportService.publishConsumer(registryURL);
            }

            return proxyFactory.getProxy(this.invoker);
        }
    }

这其实就是ReferenceConfig 中init整个流程了,先检查配置,通过配置构建一个 map ,然后利用 map 来构建 URL ,再通过 URL 上的协议利用自适应扩展机制调用对应的 protocol.refer 得到相应的 invoker 。

在有多个 URL 的时候,先遍历构建出 invoker 然后再由 StaticDirectory 封装一下,然后通过 cluster 进行合并,只暴露出一个 invoker 。

然后再构建代理,封装 invoker 返回服务引用,之后 Comsumer 调用的就是这个代理类。

在这里插入图片描述

refprotocol.refer

在这里插入图片描述

这个时候协议是registry ,故先调用是RegistryProtocol#refer

 public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        url = URLBuilder.from(url).setProtocol(url.getParameter("registry", "dubbo")).removeParameter("registry").build();
        Registry registry = this.registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return this.proxyFactory.getInvoker(registry, type, url);
        } else {
            Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded("refer"));
            String group = (String)qs.get("group");
            //根据是否有group 调用不同cluster调用doRefer
            return group == null || group.length() <= 0 || Constants.COMMA_SPLIT_PATTERN.split(group).length <= 1 && !"*".equals(group) ? this.doRefer(this.cluster, registry, type, url) : this.doRefer(this.getMergeableCluster(), registry, type, url);
        }
    }

RegistryProtocol#doRefer 进行真正的引用

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        RegistryDirectory<T> directory = new RegistryDirectory(type, url);
        //塞入注册中心
        directory.setRegistry(registry);
        
        directory.setProtocol(this.protocol);
        Map<String, String> parameters = new HashMap(directory.getUrl().getParameters());
        //生成服务消费者链接
        URL subscribeUrl = new URL("consumer", (String)parameters.remove("register.ip"), 0, type.getName(), parameters);
        if (!"*".equals(url.getServiceInterface()) && url.getParameter("register", true)) {
        //想注册中心注入服务消费者,在consumers创建新节点
            directory.setRegisteredConsumerUrl(this.getRegisteredConsumerUrl(subscribeUrl, url));
            registry.register(directory.getRegisteredConsumerUrl());
        }

        directory.buildRouterChain(subscribeUrl);
        //在订阅注册中的providers目录、configurators 和routers目录
        directory.subscribe(subscribeUrl.addParameter("category", "providers,configurators,routers"));
        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }


Conusmer 是在 RegistryProtocol#refer 中向注册中心注册自己的信息,并且订阅 Provider 和配置的一些相关信息

封装之后invoker
在这里插入图片描述

DubboProtocol#refer

具体调用哪个 protocol 还是得看 URL的协议的,我们这里是 dubbo 协议
在这个getClients(url) 中有实例类型为 ExchangeClient,底层依赖 Netty 来进行网络通信,这里不再深入了

public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
        this.optimizeSerialization(url);
        //拿到url创建client 在这个getClients(url) 中有实例类型为 ExchangeClient,底层依赖 Netty 来进行网络通信,这里不再深入了
        DubboInvoker<T> invoker = new DubboInvoker(serviceType, url, this.getClients(url), this.invokers);
        this.invokers.add(invoker);
        return invoker;
    }

在这里插入图片描述

这个是refprotocol.refer 这一段的流程图,我们将整个流程图贴出来大家参考一下
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值