!
也想出现在这里? 联系我们
广告位
当前位置:首页>程序源码>程序源码怎么导出来(解读dubbo服务导出源码分析)

程序源码怎么导出来(解读dubbo服务导出源码分析)

开头

上一节讲到了dubbo是如何和spring进行集成的,里面讲解了如何加载@Service和@Reference注解,这一节开始讲dubbo的核心源码-服务导出,也就是说,服务端要将自己的接口暴露,解析为一个可供消费端调用的远程服务。

主要流程

1.入口,
ServiceBean.onApplicationEvent,ServiceBean监听了spring启动事件,启动完后会调用此方法进行服务导出
2.配置参数准备,如优先级、覆盖等,因为配置参数可以在消费端和服务端同时配置,所以需要合并、取优先级等
3.根据服务的url、参数、协议等信息启动通信服务器,如tomcat、netty等
4.将服务信息注册到注册中心-register protocol
5.监听路径registry.subscribe

源码流程

1.总入口

上一节讲到了,spring集成dubbo的时候,服务端的bean会生成一个ServiceBean,这个ServiceBean实现了ApplicationListener接口,在spring启动完后会调用onApplicationEvent方法

@Override    public void onApplicationEvent(ContextRefreshedEvent event) {        if (!isExported() && !isUnexported()) {            if (logger.isInfoEnabled()) {                logger.info("The service ready on spring started. service: " + getInterface());            }            // 服务导出很重要(服务注册)            export();        }    }

2.配置参数准备

在export方法中,第一行就是配置参数检查和修改 checkAndUpdateSubConfigs();参数很多,就不点进去看了

public synchronized void export() {        checkAndUpdateSubConfigs();              if (!shouldExport()) {            return;        }           if (shouldDelay()) {            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);        } else {            // 导出服务            doExport();        }    }

3.根据服务的url、参数、协议等信息启动通信服务器,如tomcat、netty等

从2方法点进去doExport->doExportUrls->doExportUrlsFor1Protocol(重点),来到这个方法的最后一行,这里的protocol是一个Protocol的SPI类,调用protocol.export首先会调用protocol的包装类
ProtocolFilterWrapper.export方法,由于这时的protocol类型是register类型,所以会调用RegisterProtocol.export方法。

                    Exporter<?> exporter = protocol.export(wrapperInvoker); @Override    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {        if (REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {            return protocol.export(invoker);        }        return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));    }

在RegisterProtocol.export方法中,有一行启动通信服务器的代码,这里进去又会调用protocol.export方法,由于现在采用是dubbo协议,所以会调用DubboProtocol.export方法

        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

DubboProtocol.export方法,可以看到这里会启动netty服务器

@Override        public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {        URL url = invoker.getUrl();        //省略非核心代码..        // 开启NettyServer        openServer(url);         return exporter;    }

4.将服务信息注册到注册中心

启动完netty后,再回到RegisterProtocol.export方法,这里就是把接口注册到配置中心上面去

if (register) {            // 注册服务            register(registryUrl, registeredProviderUrl);               }

测试的时候采用的注册中心是zk,最后会调用
ZookeeperRegistry.doRegister方法

public void register(URL registryUrl, URL registeredProviderUrl) {        Registry registry = registryFactory.getRegistry(registryUrl);        // 调用ZookeeperRegistry的register方法        registry.register(registeredProviderUrl);    }

ZookeeperRegistry.doRegister,可以看到这里会使用zk客户端向zk服务器创建节点,将服务信息注册到zk,最终在zk上面看到的节点就是有一个providers文件夹,下面有我们的demoService服务

 @Override    public void doRegister(URL url) {        try {            zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));        } catch (Throwable e) {            throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);        }    }

5.监听路径registry.subscribe

在RegisterProtocol.export方法中,这里会监听zk节点,调用链为FailBackRegistry->
ZookeeperRegistry.doSubscribe->notify通知所有的监听器OverrideListener,比如url有变化,会进行服务重新导出

 registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

难点

整体就是围绕protocol.export方法进行展开调用的,那么每次调用的时候dubbo又是怎么知道这个时候的protocol是registry还是dubbo类型的呢,由于采用的spi机制,会生成一个protocol的包装类,跟踪源码,个protocol的包装类代码如下
可以看export方法,在调用export方法的时候,每次会调用arg0.getUrl方法,这里的url就包含了协议的类型,url比如:
registry://xxxxxxxxxxxxxxx,dubbo://xxxxxxxxxxxxxxxxxxxx

package org.apache.dubbo.rpc;import org.apache.dubbo.common.extension.ExtensionLoader;public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {public void destroy()  {throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public int getDefaultPort()  {throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");}public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");org.apache.dubbo.common.URL url = arg0.getUrl();String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);return extension.export(arg0);}public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {if (arg1 == null) throw new IllegalArgumentException("url == null");org.apache.dubbo.common.URL url = arg1;String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);return extension.refer(arg0, arg1);}}

总结

核心就是protocol.export,需要理解SPI机制的调用。
到这一步就完成了从普通接口注册到配置中心,并且提供dubbo服务的流程。

给TA打赏
共{{data.count}}人
人已打赏
程序源码

程序源码怎么找(解读步骤编译开源代码)

2022-8-2 16:58:00

系统运维

系统运维要会什么语言(分享自动化运维系统用编程语言)

2022-7-23 16:02:44

声明 本站上的部份代码及教程来源于互联网,仅供网友学习交流,若您喜欢本文可附上原文链接随意转载。无意侵害您的权益,请发送邮件至 admin@s9h.cn 或点击右侧 私信:少羽 反馈,我们将尽快处理。
0 条回复A文章作者M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索