Dubbo源码随记

先分析XML方式启动Spring和Dubbo
下载Dubbo源码后我们主要从dubbo-demo->dubbo-demo-xml模块中分析源码

1.先分析dubbo-demo-xml-provider项目
分析之前先修改下log4j的配置文件输出更多内容方便代码跟踪分析
先修改日志输出为Debug
log4j.rootLogger=debug, stdout
在输出内容上
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2} %M %m%n =>
在%c{2} 后面增加%M 增加方法输出
先看配置文件:dubbo-provider.xml
里面标签基本是<dubbo:***> 是dubbo扩展了Spring的schema.这个属于spring源码部分不做解释,看Dubbo源码前有必要去看下Spring的源码不然很多地方不会理解,从标签找到Spring的NamespaceHandlerSupport.定义内容如下:
public void init() {
    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
    registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
    registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
    registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
    registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
    registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
    registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
    registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
    registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
}
这里就是Dubbo扩展的标签,通过Spring解析后每个标签对应的类,比如service标签解析后会变成ServiceBean类举个例子吧..
<dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/>这个解析过后差不多和
<bean id="org.apache.dubbo.demo.DemoService" class="com.alibaba.dubbo.config.spring.ServiceBean">
    <property name="interface" value="org.apache.dubbo.demo.DemoService"/>
    <property name="ref" ref="demoService"/>
</bean> 差不多吧,实际上有很多其他的配置
有兴趣可以看下每个配置文件的配置信息.
mian方法中只有两句代码很单间:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
context.start();
第一行代码一般是Spring准备上下文
这里可以复习下Spring源码启动过程
跟踪ClassPathXmlApplicationContext类的构造方法可以在类中跟踪到refresh()方法
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            //准备刷新的上下文环境,例如对系统属性或者环境变量进行准备及验证。
            prepareRefresh();
            //初始化BeanFactory,并进行XML文件读取,
            //这一步之后,ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是可以进行Bean的提取等基础操作了。
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            //对BeanFactory进行各种功能填充,@Qualifier与@Autowired这两个注解正是在这一步骤中增加的支持。
            //设置@Autowired和 @Qualifier注解解析器QualifierAnnotationAutowireCandidateResolver       prepareBeanFactory(beanFactory);
            try {
                //子类覆盖方法做额外的处理,提供了一个空的函数实现postProcessBeanFactory来方便程序员在业务上做进一步扩展。
                postProcessBeanFactory(beanFactory);
                //激活各种BeanFactory处理器
                invokeBeanFactoryPostProcessors(beanFactory);
                //注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用是在getBean时候
                registerBeanPostProcessors(beanFactory);
                //为上下文初始化Message源,即不同语言的消息体进行国际化处理
                initMessageSource();
                //初始化应用消息广播器,并放入“applicationEventMulticaster”bean中
                initApplicationEventMulticaster();
                //留给子类来初始化其它的Bean
                onRefresh();
                //在所有注册的bean中查找Listener bean,注册到消息广播器中
                registerListeners();
                //初始化剩下的单实例(非惰性的)
                finishBeanFactoryInitialization(beanFactory);
                //完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
                finishRefresh();
            }
            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }
在这个方法里看到spring基本的启动流程包括类的注册都在这里完成
在这个方法最后的finishRefresh()方法里可以看到this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this))); 发出了一个ContextRefreshEvent事件
根据以往经验,事件都是广播异步的执行所以直接代码跟踪是跟踪不了的..然后我们只需要在 this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this))); 这句代码上加个断点.然后跑程序在到达这个断点前的控制台输出清除然后再继续跑程序就会看到这个广播会触发哪些事件方法.
[06/06/20 11:31:57:920 CST] main DEBUG support.DefaultListableBeanFactory doGetBean: Returning cached instance of singleton bean 'org.apache.dubbo.demo.DemoTwoService'
[06/06/20 11:31:57:930 CST] main  INFO config.AbstractConfig onApplicationEvent:  [DUBBO] The service ready on spring started. service: org.apache.dubbo.demo.DemoService, dubbo version: , current host: 192.168.220.1
.....
可见执行了 this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this))); 发放后打印了以上几句..然后我们分析下第一句代码是spring返回一个DemoService对象,
Returning cached instance of singleton bean 'org.apache.dubbo.demo.DemoService' 在Spring的源码里查找是AbstractBeanFactory类中的 doGetBean 方法打印的.看过Spring源码的都很熟悉这个方法是干嘛的.不过本文是Dubbo源码分析就不详细说明了.
第二句The service ready on spring started....是在AbstractConfig的onApplicaEvent 这个熟悉Spring的一定就能马上想到ApplicationListener..
好了我们现在找onApplicationEvent这个方法...过程挺蛋疼输出是 AbstractConfig..但是你去这个类实际上找不到..原因就是程序员偷懒了..在真正输出的地方logger.info("The service ready on spring started. service: " + getInterface());这个logger对象是从AbstractCofnig继承下来的..所以.....反正我每次都是获取本类的class生产的Logger....通过查找The service ready on spring started这句话我们找到这个方法是在ServiceBean中被调用的..查看onApplicationEvent方法可以发现export()方法.跟踪这个方法的调用,进入export()->doExportUrls()->doExport()->doExportUrls()..中间很多方法都是check检查参数啥的就略过去了.有空看看也没啥.
分析doExportUrls方法:

    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);
        for (ProtocolConfig protocolConfig : protocols) {
            String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
            logger.debug("pathKey:"+pathKey);
            ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
            ApplicationModel.initProviderModel(pathKey, providerModel);
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

List<URL> registryURLs = loadRegistries(true); 
查看loadRegistries方法很容易发现这个是加载Dubbo的注册中心配置:有意思的是在这发现了一段代码:for (RegistryConfig config : registries) 便利.可见Dubbo是支持多注册中心的.具体用法百度下.其实看这段代码也能看出来.多搞几个<register >标签配置上就行了 因为上面提过<dubbo:register> 解析完就是RegistryConfig类.
继续看
for (ProtocolConfig protocolConfig : protocols) 会发现Dubbo也支持多协议..主要看doExportUrlsFor1Protocol方法该方法略长
在if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)之前基本都是准备参数.自己看下 导出服务时的参数主要来源于哪些配置:
这段代码我刚开始看还以为是扩展类加载器= =,后面自己想了下不太对 这玩意和类加载器根本不沾边 这东西是Dubbo的扩展加载主类,至于这玩意是啥可以看另外一篇文章 Dubbo的扩展加载机制 如果不看你可以当Java的SPI理解,差不多的东西.
这里注意下 
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
Dubbo会根据协议去配置url,Dubbo是使用URL作为配置载体的,通过URL可以让Dubbo的配置在各个模块传递.
再往下面是对scope进行判断,也不难分析. scope为none时不导出(export),当不指定remote时是导出到本地,不为local导出远程.
看下导出本地方法:
     private void exportLocal(URL url) {
        URL local = URLBuilder.from(url)
                .setProtocol(LOCAL_PROTOCOL)
                .setHost(LOCALHOST_VALUE)
                .setPort(0)
                .build();
        Exporter<?> exporter = protocol.export(
                PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
        exporters.add(exporter);
        logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
    }
先找下protocol和PROXY_FACTORY 这里都是用的dubbo默认的,protocol网上很多说是使用的是InjvmProtocol,其实并不是看其初始化代码ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(),这个ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()这个类是生成一个适配器类,在执行export的时候会动态去加载适合的Protocol,  PROXY_FACTORY和protocol是一样的.不过在执行过程中到处到本地会用InjvmProtocol 导出到远程会根据Invoker的Url属性去动态选择相应的Protocol.可以看做protocol默认的是InjvmProtocol PROXY_FACTORY默认的是JavassistProxyFactory
本地导出没什么难的主要几个步骤 
1.形成新的local URL
2.使用代理工厂生产一个Invoker,Invoker网上有很多文章解释 随便看看,官方文档也可以看看.其实就是封装一个类.其实你看下生成类的源码就行.在CLassGenerator的toClass方法打印mClassName,mInterfaces,mFields,mMethods,mConstructors的内容就行.然后就非常好理解了. 这个生成的类有invokeMethod看下这个方法.不难没啥难理解的地方,Dubbo默认的PROXY_FACTORY是JavassistProxyFactory官方文档上有
3.执行InjvmProtocol的export方法,改方法也简单就是简单的创建了一个InjvmExporter对象
4.加入本地导出服务缓存

然后看下导出服务到远程的代码,比导出本地要复杂不少,不过也不难
            if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
                if (!isOnlyInJvm() && logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (CollectionUtils.isNotEmpty(registryURLs)) {
                    for (URL registryURL : registryURLs) {  //循环注册中心
                        //if protocol is only injvm ,not register  如果该服务指明注册中心使用injvm,则不进行注册
                        if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                            continue;
                        }
                        url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }

                        // For providers, this is used to enable custom proxy to generate invoker
                        String proxy = url.getParameter(PROXY_KEY);
                        if (StringUtils.isNotEmpty(proxy)) {
                            registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                        }

                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));//Invoker的生产和之前说的没区别,只不过加了个RUL参数过去
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this); //这里是封装Invoker和当前ServiceConfig , DelegateProviderMetaDataInvoker就是个元组,此类还实现了Invoker接口,适配器模式?
                        Exporter<?> exporter = protocol.export(wrapperInvoker);  //这里就是我刚才说的那个情况,在上面代码 export会执行InjvmProtocol的export方法,在这里会根据wrapperInvoker的Url中的protocol去选择合适的加载这里使用的是registry然后在配置文件里查找registry对应的Protocol,在dubbo-registry的dubbo-registry-api的resource/META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol找到答案registry=org.apache.dubbo.registry.integration.RegistryProtocol   dubbo就是默认使用这个类去导出服务到远程的.接下来重点分析这个类的内容.
                        exporters.add(exporter);
                    }
                } else {
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
                /**
                 * @since 2.7.0
                 * ServiceData Store
                 */
                MetadataReportService metadataReportService = null;
                if ((metadataReportService = getMetadataReportService()) != null) {
                    metadataReportService.publishProvider(url);
                }
            }
RegistryProtocol的export方法也不难分析
@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {    
        URL registryUrl = getRegistryUrl(originInvoker);//获取注册中心URL
        URL providerUrl = getProviderUrl(originInvoker); //获得服务提供者的URL
        // 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);
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl); //导出服务
        // url to registry
        final Registry registry = getRegistry(originInvoker); //获得注册器
        final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl); //提供者注册的URL
        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);
    }
doLocalExport:
  private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
        String key = getCacheKey(originInvoker);
        return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {   
            Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
            return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
        });
    }
服务的导出关键在protocol.export(invokerDelegate)  这里的protocol依然是之前的说的,可以根据URL的protocol去执行不同Protocol的export,这个可以看生成这个protocol的代码得出来的,这里我是改了个源码让这种问题更好发现= =,
private static final String CODE_EXT_NAME_ASSIGNMENT = "String extName = %s;\nSystem.err.println(extName);\n"; 我在生成的判断 语句加了个err输出.这样执行到 protocol.export(invokerDelegate) 的时候会打印出protocol的值,这里打印了dubbo所以就很容易判断是执行了具体哪个类的export
这里使用的是dubbo默认的协议dubbo在源码里查询dubbo=  会 查询出来唯一的Protocol为dubbo的dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol,看下这个类的export具体干了什么.
主要openServer方法.这个方法本身也没什么太多东西,其实主要就是创建服务器,具体是在 Exchangers.bind 方法打开的,其中有这么句代码ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type); 这个type值是header找下header的Exchanger执行的是bind方法
    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }
嵌套的方法都是在封装对象参数之类 主要是 Transporters.bind方法..这个方法最后调用了getTransporter() 这个也和Protocol一样是生成了Transporter自适配类.
生成的代码显示是获取的URL的transporter字段查找服务器Transport的,因为Transporter类的SPI注解指定了netty字符串,说明默认使用nettyTransoport找下netty=查找到的文件里面是
netty4=org.apache.dubbo.remoting.transport.netty4.NettyTransporter
netty =org.apache.dubbo.remoting.transport.netty4.NettyTransporter
说明dubbo使用的是netty4作为默认服务器. 查看NettyTransporter类的bind方法,很明显是创建netty服务器.就不做详细分析了,玩过netty的也不难看懂这块的代码

现在回到RegistryProtocol的export方法中,接下来要到注册中心去注册服务了.
final Registry registry = getRegistry(originInvoker);
这个方法是获取注册getRegistry代码也是一样是一个RegistryFactory的自适配器,这里我使用的zookeeper注册中心,所以获取的是ZookeeperRegistryFactory调用registryFactory.getRegistry产生的是ZookeeperRegistry,跟踪代码
         if (register) {
            register(registryUrl, registeredProviderUrl);
            providerInvokerWrapper.setReg(true);
        }
这里是注册服务的地方,跟踪进去也不难找到Dubbo如何注册到zookeeper上的.

看下服务的消费:
看下dubbo的demo
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
        context.start();
        DemoService demoService = context.getBean("demoService", DemoService.class);
        String hello = demoService.sayHello("world");
在服务的使用中,服务和普通的spring bean没有区别,从容器中直接getBean获取类调用该方法即可.
配置服务引用的配置如下
<dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoService"/>
之前在服务提供流程源码分析过<dubbo:reference >标签会解析成为 ReferenceBean 类,看下ReferenceBean的组成extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean
实现了FactoryBean标志改类为工厂类,在获取bean的时候会调用该方法的getObject方法获取类.跟踪getObject方法
get()->init()->createProxy() 
    private T createProxy(Map<String, String> map) {
        if (shouldJvmRefer(map)) { // 本地引用
            URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
            invoker = REF_PROTOCOL.refer(interfaceClass, url);  // 调用 refer 方法构建 InjvmInvoker 实例
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
        } else {    // 远程引用
            urls.clear(); // reference retry init will add url to urls, lead to OOM
            if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address. url是ReferenceConfig也就是<dubbo:reference>的url属性,在文档找到该属性解释就理解这块了
                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 (REGISTRY_PROTOCOL.equals(url.getProtocol())) {     // 检测 url 协议是否为 registry,若是,表明用户想使用指定的注册中心
                        // 将 map 转换为查询字符串,并作为 refer 参数的值添加到 url 中
                            urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
        // 合并 url,移除服务提供者的一些配置(这些配置来源于用户配置的 url 属性),
                            // 比如线程池相关配置。并保留服务提供者的部分配置,比如版本,group,时间戳等
                            // 最后将合并后的配置设置为 url 查询字符串中
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else { // assemble URL from register center's configuration   装配注册中心配置也就是<dubbo:register>
                // if protocols not injvm checkRegistry
                if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())){ //判断不是injvm协议
                    checkRegistry();
                    List<URL> us = loadRegistries(false);
                    if (CollectionUtils.isNotEmpty(us)) {
                        for (URL u : us) {
                            URL monitorUrl = loadMonitor(u);
                            if (monitorUrl != null) {
                                map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                            }
                            urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                        }
                    }
                    if (urls.isEmpty()) {
                        throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
                    }
                }
            }

            if (urls.size() == 1) { //单注册中心
                invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0)); //调用protocol的refer方法.之前讲过是使用url的protocol来判断使用哪个protocol,这里是registry
            } else { //多注册中心
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
                    invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
                    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // use last registry url
                    }
                }
    //下面使用Cluster合并多个注册中心
                if (registryURL != null) { // registry url is available
                    // use RegistryAwareCluster only when register's CLUSTER is available
                    URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
                    // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
                    invoker = CLUSTER.join(new StaticDirectory(u, invokers));
                } else { // not a registry url, must be direct invoke.
                    invoker = CLUSTER.join(new StaticDirectory(invokers));
                }
            }
        }

        if (shouldCheck() && !invoker.isAvailable()) {
            throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
        }
        if (logger.isInfoEnabled()) {
            logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
        }
        /**
         * @since 2.7.0
         * ServiceData Store
         */
        MetadataReportService metadataReportService = null;
        if ((metadataReportService = getMetadataReportService()) != null) {
            URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
            metadataReportService.publishConsumer(consumerURL);
        }
        // create service proxy
        return (T) PROXY_FACTORY.getProxy(invoker);
    }
主要研究下如何导入服务,这里就不去研究多注册中心了我们直接看单注册中心如何导入服务.这里直接使用REF_PROTOCOL.refer获取一个Invoker,查看如何创建的Invoker,根据之前的分析 这里的REF_PROTOCOL.refer是调用的RegistryProtocol的refer方法方法如下
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();
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }
        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);
            }
        }
        return doRefer(cluster, registry, type, url);
    }
在refer方法里第一件事就获取注册中心,我们配置的是zookeeper所以调用了ZookeeperRegistryFactory的getRegistry方法获取了ZookeeperRegistry.在ZookeeperRegistry构造里连接了zookeeper..
回头看下RegistryProtocol的refer方法看源码看的多的一眼就能看到doRefer一般来说真正做事的都是doXX方法..
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        //创建RegistryDirectory
        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;
    }
如上,doRefer 方法创建一个 RegistryDirectory 实例,然后生成服务者消费者链接,并向注册中心进行注册。注册完毕后,紧接着订阅 providers、configurators、routers 等节点下的数据。完成订阅后,RegistryDirectory 会收到这几个节点下的子节点信息。由于一个服务可能部署在多台服务器上,这样就会在 providers 产生多个节点,这个时候就需要 Cluster 将多个服务节点合并为一个,并生成一个 Invoker。
有了Invoker就可以调用PROXY_FACTORY.getProxy(invoker);生成对象的代理了.
PROXY_FACTORY和之前一样实际调用的是JavassistProxyFactory的getProxy..别找错了 JavassistProxyFactory里面的getProxy有俩参数,真正调用的是父类的方法.在父类的调用路径中会最终会调用JavassistProxyFactory的getProxy生成代理类在该方法中调用Proxy的getProxy


获取代理的方法调用栈: ReferenceBean.getObject->ReferenceConfig.get->ReferenceConfig.init->ReferenceConfig.createProxy->JavassistProxyFactory.getProxy方法
DubboProtocol是实现了Dubbo通信协议的
具体调用在DubboInvoker的doInvoke方法里调用,
需要实现    
    Invoker 继承AbstractInvoker
    DubboProtocol 继承AbstractProtocol

RegistryDirectory的NotifyListener实现了notify方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值