Fork me on GitHub

Dubbo暴露服务过程

前言

dubbo框架也用了有一年了,一直没有详细的研究过dubbo源码。所以趁有时间好好学习体会dubbo的博大精深。本人才疏学浅,如有不对,请大神指点。这里使用的dubbo版本是2.6.1。

如何看源码?跟着Dubbo开发手册(中文)来喽。带着目的看源码,这次看dubbo是怎么暴露服务的。

先瞜一眼启动日志

一般像这种大型的开源框架,都会有健全的启动日志,看看日志输出利于我们理解dubbo启动流程。 日志输出从上往下看,dubbo做了哪些事:

  1. 暴露本地服务
  2. 暴露远程服务
  3. 启动Netty,绑定和暴露地址
  4. 连接zookeeper
  5. zookeeper订阅服务
  6. 监听zookeeper

先瞜一眼官方手册

这段内容来自dubbo开发手册之实现细节

再来一段暴露服务时序图

接下来,从官方文档开始,分析dubbo服务暴露过程。

第一步, ServiceConfig

分析前,先利用IDE生成类图看看ServiceConfig的继承关系。

问题一:这么多的配置是啥?
凭借感觉像是和dubbo.xml里的配置属性有关系。先不管,留个坑。

根据时序图,我们先定位到 ServiceConfig 的 export()方法 ServiceConfig#export

public synchronized void export() {
    ...
    // 延迟暴露接口
    if (delay != null && delay > 0) {
        delayExportExecutor.schedule(new Runnable() {
            public void run() {
                doExport();
            }
        }, delay, TimeUnit.MILLISECONDS);
    } else {
        doExport(); // 此处调用开始暴露
    }
}

暴露服务是调用 ServiceConfig#doExport方法

protected synchronized void doExport() {
    if (unexported) {
        throw new IllegalStateException("Already unexported!");
    }
    if (exported) {
        return;
    }
    exported = true;
    if (interfaceName == null || interfaceName.length() == 0) {
        throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
    }
    checkDefault();// 创建了 ProviderConfig 对象并赋值 setter is属性,提供者的缺省值设置
    /**
     * provider已经配置的情况下,application、module、registries、monitor、protocol中未配置的值均可以从provider获取
     */
    if (provider != null) {
        if (application == null) {
            application = provider.getApplication();
        }
        if (module == null) {
            module = provider.getModule();
        }
        if (registries == null) {
            registries = provider.getRegistries();
        }
        if (monitor == null) {
            monitor = provider.getMonitor();
        }
        if (protocols == null) {
            protocols = provider.getProtocols();
        }
    }
    if (module != null) {
        if (registries == null) {
            registries = module.getRegistries();
        }
        if (monitor == null) {
            monitor = module.getMonitor();
        }
    }
    if (application != null) {
        if (registries == null) {
            registries = application.getRegistries();
        }
        if (monitor == null) {
            monitor = application.getMonitor();
        }
    }
    if (ref instanceof GenericService) {
        interfaceClass = GenericService.class;
        if (StringUtils.isEmpty(generic)) {
            generic = Boolean.TRUE.toString();
        }
    } else {
        try {
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                    .getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        checkInterfaceAndMethods(interfaceClass, methods); // 检查配置中的 interface 属性 和 methods属性
        checkRef();  // 检查 ref 属性
        generic = Boolean.FALSE.toString();
    }
    // 如果配置 local 属性, 是否服务接口客户端本地代理
    if (local != null) {
        if ("true".equals(local)) {
            local = interfaceName + "Local";
        }
        Class<?> localClass;
        try {
            localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if (!interfaceClass.isAssignableFrom(localClass)) {
            throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
        }
    }
    // 如果配置 stub 属性, 是否本地存根 
    if (stub != null) {
        if ("true".equals(stub)) {
            stub = interfaceName + "Stub";
        }
        Class<?> stubClass;
        try {
            stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        if (!interfaceClass.isAssignableFrom(stubClass)) {
            throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
        }
    }
    checkApplication(); // 检查 application 属性
    checkRegistry(); // 检查 registry 属性
    checkProtocol(); // 检查 protocol 属性
    appendProperties(this); // 赋值 ServiceConfig setter is 属性
    checkStubAndMock(interfaceClass); // 检查是否 使用 local,stub,mock 代理
    if (path == null || path.length() == 0) {
        path = interfaceName;
    }
    doExportUrls(); // 开始暴露远程服务了
    ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
    ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
}

ServiceConfig#doExportUrls暴露多个远程地址

private void doExportUrls() {
    // dubbo支持多注册中心,所以这一步把 registry 配置信息封装为多个url,比如 registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider...
    List<URL> registryURLs = loadRegistries(true);
    // dubbo是支持多协议的,将所有注册的url上对应的协议暴露出来
    for (ProtocolConfig protocolConfig : protocols) {
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

ServiceConfig#doExportUrlsFor1Protocol暴露单个地址

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    String name = protocolConfig.getName();
    if (name == null || name.length() == 0) {
        name = "dubbo";
    }

    // map存放所有配置参数,下面生成url用
    Map<String, String> map = new HashMap<String, String>();
    map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
    map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
    map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
    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);
    // method子标签配置规则解析,暂时不管
    if (methods != null && !methods.isEmpty()) {
        for (MethodConfig method : methods) {
            ...
        } // end of methods for
    }

    // 获取所有方法添加到map中,体现在url里
    if (ProtocolUtils.isGeneric(generic)) { // 如果是泛化实现,generic属性为true,method=*表示任意方法
        map.put("generic", generic);
        map.put("methods", Constants.ANY_VALUE);
    } else {
        String revision = Version.getVersion(interfaceClass, version);
        if (revision != null && revision.length() > 0) {
            map.put("revision", revision);
        }

        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        if (methods.length == 0) {
            logger.warn("NO method found in service interface " + interfaceClass.getName());
            map.put("methods", Constants.ANY_VALUE);
        } else {
            map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }
    // 如果配置了token属性,如果配为default则随机UUID,否则使用配置中的token,作令牌验证用
    if (!ConfigUtils.isEmpty(token)) {
        if (ConfigUtils.isDefault(token)) {
            map.put("token", UUID.randomUUID().toString());
        } else {
            map.put("token", token);
        }
    }
    // 如果协议是 injvm,就不注册服务, notify设置为false
    if ("injvm".equals(protocolConfig.getName())) {
        protocolConfig.setRegister(false);
        map.put("notify", "false");
    }
    // export service
    String contextPath = protocolConfig.getContextpath();
    // 如果 protocol配置没有配置contextPath属性,就从provider配置中取
    if ((contextPath == null || contextPath.length() == 0) && provider != null) {
        contextPath = provider.getContextpath();
    }

    String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
    Integer port = this.findConfigedPorts(protocolConfig, name, map);
    // 根据上面的参数创建url对象
    URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
    // 如果url使用的协议存在扩展,调用对应的扩展来修改原url。
    if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
            .hasExtension(url.getProtocol())) {
        url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }

        String scope = url.getParameter(Constants.SCOPE_KEY);
        // 如果scope属性没有配置为 none
        if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {

            // 如果scope属性没有配置为 remote, 暴露本地服务
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }
            // // 如果scope属性没有配置为 local, 暴露远程服务
            if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }
                if (registryURLs != null && !registryURLs.isEmpty()) {
                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
                        URL monitorUrl = loadMonitor(registryURL);
                        if (monitorUrl != null) { // 如果有monitor信息,则在url上增加monitor配置
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }
                        // 重要的第二步了,创建 invoker 对象(这里暴露远程协议里,在远程协议里增加了属性 export=url,url默认dubbo协议暴露地址)
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                        // 第三步,官方文档加重点的一步,invoker转化为 exporter
                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            }
        }
        this.urls.add(url);
    }

第二步,ProxyFactory.getInvoker

ServiceConfig#doExportUrlsFor1Protocol暴露单个地址中的调用:

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

接下来看看这一行代码里做了什么。

问题二:这个 ProxyFactory$Adaptive是什么东东? 看看 proxyFactory 是怎么来的。

private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

看来是和这个 ExtensionLoader 有关。看看接口:

@SPI("javassist")
public interface ProxyFactory {
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

}

@SPI 看起来和java SPI机制有关哦。先留个坑,回头再解决。

但是通过我们debug发现,默认情况下 ProxyFactory的实现是 JavassistProxyFactory。

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

正如官方文档所说:

首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。

JavassistProxyFactory的getInvoker实现是先创建一个包装类Wrapper ,包装类来实现远程调用。简单看下这个包装类是什么吧,Wapper.makeWrapper(Class<?> c): 结果大致如下

public class Wrapper1 extends Wrapper {
    public static String[] pns;
    public static Map pts;
    public static String[] mns; // all method name array.
    public static String[] dmns;
    public static Class[] mts0;

    public String[] getPropertyNames() {
        return pns;
    }

    public boolean hasProperty(String n) {
        return pts.containsKey($1);
    }

    public Class getPropertyType(String n) {
        return (Class) pts.get($1);
    }

    public String[] getMethodNames() {
        return mns;
    }

    public String[] getDeclaredMethodNames() {
        return dmns;
    }

    public void setPropertyValue(Object o, String n, Object v) {
        dubbo.provider.hello.service.impl.HelloServiceImpl w;
        try {
            w = ((dubbo.provider.hello.service.impl.HelloServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class dubbo.provider.hello.service.impl.HelloServiceImpl.");
    }

    public Object getPropertyValue(Object o, String n) {
        dubbo.provider.hello.service.impl.HelloServiceImpl w;
        try {
            w = ((dubbo.provider.hello.service.impl.HelloServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class dubbo.provider.hello.service.impl.HelloServiceImpl.");
    }

    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        dubbo.provider.hello.service.impl.HelloServiceImpl w;
        try {
            w = ((dubbo.provider.hello.service.impl.HelloServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        try {
            if ("sayHello".equals($2) && $3.length == 0) {
                w.sayHello();
                return null;
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class dubbo.provider.hello.service.impl.HelloServiceImpl.");
    }
}

第三步,invoker转化为 exporter

ServiceConfig#doExportUrlsFor1Protocol暴露单个地址中的调用:

Exporter<?> exporter = protocol.export(wrapperInvoker);

由代码可知,这里的 protocol 和第二步里的proxyFactory 一样

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

我们再看看 Protocol接口,也是SPI机制:

@SPI("dubbo")
public interface Protocol {

    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    void destroy();

}

凭感觉会使用dubbo协议调用到DubboProtocol。先debug,果不其然。看一眼调用栈:

从ServiceConfig之后,有两次协议调用,先是调用RegistryProtocol,然后RegistryProtocol里调用了DubboProtocol。 两次暴露协议前,都会调用到 ProtocolListenerWrapper 和 ProtocolFilterWrapper,看看这两个地方。
ProtocolListenerWrapper#export方法

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // registry类型的Invoker,直接暴露
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    // 非Registry类型的Invoker,需要被监听器包装
    // 这里的protocol是ProtocolFilterWrapper
    return new ListenerExporterWrapper<T>(protocol.export(invoker),
            Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                    .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}

ProtocolFilterWrapper#export方法

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // registry类型的Invoker,直接暴露
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    //非Registry类型的Invoker需要先构建调用链,然后再暴露
    return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}

这里构建调用链,控制invoker调用执行顺序,默认的filters如下图:

暂且不谈Filter接口相关。

按照调用顺序,先调用 RegistryProtocol#export

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    //export invoker
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker); // 本地暴露

    URL registryUrl = getRegistryUrl(originInvoker); // 获取注册地址,默认是dubbo,我这里使用zookeeper

    //registry provider
    final Registry registry = getRegistry(originInvoker); // 获取注册中心, 我这里的是ZookeeperRegistry对象
    final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);// 获取注册的提供者地址

    //to judge to delay publish whether or not
    boolean register = registedProviderUrl.getParameter("register", true);

    ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);// 注册提供者消费者。

    if (register) {
        register(registryUrl, registedProviderUrl); // 注册服务
        ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true); // 标记为已注册
    }

    // 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(registedProviderUrl);
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    //保证每次暴露服务返回一个新的exporter
    return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
}

然后,上面的本地暴露 doLocalExport(originInvoker) 实际上是暴露的dubbo协议,看下DubboProtocol:

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    URL url = invoker.getUrl();

    // export service.
    String key = serviceKey(url); // 获取dubbo协议服务key,serviceGroup/serviceName:serviceVersion:port
    DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
    exporterMap.put(key, exporter);

    //export an stub service for dispatching event
    Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);  // 是否是stub事件? dubbo.stub.event属性
    Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false); // 是否回调服务? is_callback_service属性
    if (isStubSupportEvent && !isCallbackservice) { // TODO 这里暂时不分析,此处demo场景为false
        String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
        if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
            if (logger.isWarnEnabled()) {
                logger.warn(new IllegalStateException("consumer [" + url.getParameter(Constants.INTERFACE_KEY) +
                        "], has set stubproxy support event ,but no stub methods founded."));
            }
        } else {
            stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
        }
    }

    openServer(url); // 开启服务(这里默认使用netty方式)
    optimizeSerialization(url);// 优化序列化
    return exporter;
}

至此返回 exporter 之后,就完成了 invoker到exporter的转化。返回到ServiceConfig后服务发布过程到此结束。

补充

先看看之前遗留的两个问题:

问题一:AbstractConfig 衍生的子类(ServiceConfig,ProviderConfig,RegistryConfig等) ,这么多的配置类是啥?

问题二:这个 ProxyFactory$Adaptive是什么东东? 看看 proxyFactory 是怎么来的。

回答问题一:

在dubbo-config模块中,代码里的解释已经很清楚了。这里简单介绍几个抽象配置:

  • AbstractConfig:配置模板,配置解析的工具方法、公共方法,提供几个主要的方法(appendAnnotation,appendProperties,appendParameters,appendAttributes等)。
  • AbstractMethodConfig:封装了一些方法级别的相关属性
  • AbstractInterfaceConfig:封装了接口需要的属性
  • AbstractReferenceConfig:主要是引用实例的配置

再看一下dubbo-config-spring模块,与spring如何整合的:

spring.handlers文件里如是写道:

http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler

dubbo的schema标签的定义就在DubboNamespaceHandler类中:

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    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 AnnotationBeanDefinitionParser());
    }

}

所以,一目了然。

回答问题二:

这确实和dubbo插件机制有关,后面再单独写一篇文章分析。

提出问题三:dubbo怎么启动的?

本文的分析是直接从dubbo文档和启动日志起手的,那么dubbo是怎么从加载spring容器到ServiceConfig暴露服务的呢?

再回顾看下dubbo暴露服务调用栈: 还记得上面的 AbstractConfig家族的类图吗。ServiceBean继承自ServiceConfig。 ServiceBean实现了pplicationListener接口,实现方法:

public void onApplicationEvent(ContextRefreshedEvent event) {
    if (isDelay() && !isExported() && !isUnexported()) {
        if (logger.isInfoEnabled()) {
            logger.info("The service ready on spring started. service: " + getInterface());
        }
        export(); // 调用父类ServiceConfig的export()
    }
}

可见,在spring容器实例化bean完成后,发布ContextRefreshedEvent事件时调用ServiceConfig的export()方法。看看日志是不是有“The service ready on spring started. service:xxx”且在服务暴露日志前呢~

后言

至此,初步完成了dubbo服务暴露过程的解析(ServiceConfig-> Invoker->Exporter),但是上面服务暴露过程有些内容并没有详细分析,比如

  • 本地暴露与远程暴露的细枝末节
  • dubbo的扩展机制
  • 获取注册中心注册服务(zookeeper)的过程
  • 开启服务过程(Netty服务)

这些后面一点点剖析。

总结

  • dubbo暴露服务过程总体分为三步:ServiceConfig-> Invoker->Exporter.
  • AbstractConfig家族是spring与dubbo整合的核心配置
  • ServiceBean中开启spring容器加载完成后的暴露服务过程
-------------本文结束,感谢您的阅读-------------
贵在坚持,如果您觉得本文还不错,不妨打赏一下~
0%