dubbo源码四:dubbo服务暴露过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhuqiuhui/article/details/83689385
  1. dubbo标签解析

    spring通过在DubboNamespaceHandler注册dubbo解析器DubboBeanDefinitionParser,在加载Bean的时候同时解析dubbo标签并加载dubbo标签解析后的Bean

  2. 暴露dubbo服务

    dubbo标签解析完成后,会把dubbo:service标签解析解析成类型为ServiceBean的RootBeanDefinition,并进行实例化,

    <!--定义了提供方应用信息,用于计算依赖关系;在 dubbo-admin 或 dubbo-monitor 会显示这个名字,方便辨识-->
        <dubbo:application name="debbo-provider" owner="programmer" organization="dubbox"/>
        <!--使用 zookeeper 注册中心暴露服务,注意要先开启 zookeeper-->
        <dubbo:registry address="zookeeper://localhost:2181"/>
        <!-- 用dubbo协议在20880端口暴露服务 -->
        <dubbo:protocol name="dubbo" port="20880" />
        <!--使用 dubbo 协议实现定义好的 api.PermissionService 接口-->
        <dubbo:service interface="service.DemoService" ref="demoService" protocol="dubbo" />
        <!--具体实现该接口的 bean-->
        <bean id="demoService" class="Impl.DemoServiceImpl"/>
    

    由于ServiceBean实现了InitializingBean和ApplicationListener接口,一个是在类初始化完成后调用afterPropertiesSet方法,一个在Spring容器初始化完成后会调用onApplicationEvent方法(调用参考bean的加载过程),即重要的就是这两个方法,这两个方法

    public void afterPropertiesSet() throws Exception {
            // ServiceConfig根据dubbo:provider标签设置provider
            // ServiceConfig根据dubbo:application标签设置应用信息配置
            // SerivceConfig根据dubbo:module标签设置模块信息配置
            // SerivceConfig设置注册中心
            // SerivceConfig根据dubbo:monitor标签设置监控中心配置
            // SerivceConfig设置服务提供者协议配置
         
            if (! isDelay()) {
                export();
            }
    }
    
    public void onApplicationEvent(ApplicationEvent event) {
        if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) {
            if (isDelay() && ! isExported() && ! isUnexported()) {
                export();
            }
        }
    }
    
    public synchronized void export() {
        //省略代码,从provider中获取delay
        if (delay != null && delay > 0) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    // 睡delay时间
                    Thread.sleep(delay);
                    // 暴露服务
                    doExport();
                }
            });
            thread.setDaemon(true);
            thread.setName("DelayExportServiceThread");
            thread.start();
        } else {
            // 没有设置delay,直接暴露服务
            doExport();
        }
    }
    
    protected synchronized void doExport() {
        // 省略大部分校验代码......
        doExportUrls();
    }
    
    
    private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);
        // 按照不同的Protocol如(dubbo,injvm)来暴露服务
        for (ProtocolConfig protocolConfig : protocols) {
            // 1.如果协议类型为null,则默认为dubbo协议
            String name = protocolConfig.getName();
            if (name == null || name.length() == 0) {
                name = "dubbo";
            }
            // 2.获取主机名host(省略代码)
            // 3.获取端口号port(省略代码)
            // 4.参数设置到map中
            Map<String, String> map = new HashMap<String, String>();
            // URL中的side属性,有两个值,一个provider,一个consumer,暴露服务的时候为provider
            map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
            // dubbo的版本号  url中的dubbo
            map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
            // url中的timestamp
            map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
            //url中的pid
            if (ConfigUtils.getPid() > 0) {
                map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
            }
            //从其他参数中获取参数
            appendParameters(map, application);
            appendParameters(map, module);
            appendParameters(map, provider, Constants.DEFAULT_KEY);
            appendParameters(map, protocolConfig);
            appendParameters(map, this);
            // 从暴露的方法配置信息中获取参数和值(代码省略)
            // 通用服务接口和非通用服务接口的参数
            if (generic) {
                // 通用服务接口
                map.put("generic", String.valueOf(true));
                map.put("methods", Constants.ANY_VALUE);
            } else {
                String revision = Version.getVersion(interfaceClass, version);
                if (revision != null && revision.length() > 0) {
                    map.put("revision", revision);
                }
                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(Wrapper.getWrapper(interfaceClass).getDeclaredMethodNames())), ","));
            }
            //5.token 临牌校验(代码省略)
            //6.injvm协议处理(代码省略)
            //7.导出服务
            // 获取上下文路径
            String contextPath = protocolConfig.getContextpath();
            if ((contextPath == null || contextPath.length() == 0) && provider != null) {
                contextPath = provider.getContextpath();
            }
            // 组装URL
            URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
            // 如果url使用的协议存在扩展,调用对应的扩展来修改原url(代码省略)
            String scope = url.getParameter(Constants.SCOPE_KEY);
            //配置为none不暴露
            if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
                //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
                if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                    exportLocal(url);
                }
                //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露本地服务)
                if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
                    if (registryURLs != null && registryURLs.size() > 0 && url.getParameter("register", true)) {
                        for (URL registryURL : registryURLs) {
                            // 省略部分代码
    	                    // 通过代理工厂将ref对象转化成invoker对象
                            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                            // 暴露服务(DubboProtocol来暴露服务,)
                            Exporter<?> exporter = protocol.export(invoker);
                            //一个服务可能有多个提供者,保存在一起
                            exporters.add(exporter);
                        }
                    } else {
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }
                }
            }
            this.urls.add(url);
        }
    }
    

    注:<dubbo:reference…scope=“remote”/>,scope为remote表示引用远程服务;为local表示引用本地服务;

    DubboProtocol中暴露服务:

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        // 获取URL
        URL url = invoker.getUrl();
        
        // export service.
        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.put(key, exporter);
            
        //export an stub service for dispaching event(代码省略)
        
        openServer(url);
        
        return exporter;
    }
    
    private void openServer(URL url) {
        // 查找服务
        String key = url.getAddress();
        
        //client 也可以暴露一个只有server可以调用的服务,所以需要判断是否是服务端
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
        if (isServer) {
            ExchangeServer server = serverMap.get(key);
            if (server == null) {
                //如果服务不存在,创建服务
                serverMap.put(key, getServer(url));
            } else {
                //server支持reset,配合override功能使用
                server.reset(url);
            }
        }
    }
    
    private ExchangeServer getServer(URL url) {
        // 忽略部分代码
        ExchangeServer server;
        server = Exchangers.bind(url, requestHandler);
        return server;
    }
    
    // 上述最终会调用到Netty
    public class NettyTransporter implements Transporter {
    
        public static final String NAME = "netty";
        
        public Server bind(URL url, ChannelHandler listener) throws RemotingException {
            return new NettyServer(url, listener);
        }
        // ......
    }
    

我的修行之路,欢迎讨论!
微信:bboyzqh
邮箱:[email protected]

猜你喜欢

转载自blog.csdn.net/zhuqiuhui/article/details/83689385