07-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 Exporter export(Invoker 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 exporter = doLocalExport(originInvoker, providerUrl);
DubboProtocol.export方法,可以看到这里会启动netty服务器
 

@Override
        public Exporter export(Invoker 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服务的流程。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值