Dubbo源码通~服务暴露之本地暴露

Dubbo服务暴露~本地暴露

功能:具体服务转换成Invoker,Invoker 转换成 Exporter
Dubbo源码通~服务暴露之本地暴露_第1张图片

1、逻辑简述

  • Spring服务暴露入口:ServiceBean.onApplicationEvent() -> ServiceBean.export()
    • Spring容器发布刷新事件,调用Dubbo的export()会进行服务暴露。
  • Dubbo服务暴露入口:ServiceConfig.export()

1.1、主要步骤

  1. 检查参数,组装URL
    • loadRegistries()
    • parseURLs()
  2. 导出服务到本地InJvm和远程
    • 远程暴露包含两种情况:
      • 有注册中心时:暴露到注册中心
      • 无注册中心时:仅仅暴露服务(最终存放到exporters中,和本地暴露一样,在URL上有些许不同)
  3. 导出服务到注册中心

1.2、服务暴露示意图

  • 服务暴露过程示意图:具体服务转换成Invoker、Invoker转换为Exporter
    Dubbo源码通~服务暴露之本地暴露_第2张图片

  • 服务暴露时序图Dubbo源码通~服务暴露之本地暴露_第3张图片


2、服务暴露入口

onApplicationEvent方法会等Spring发起容器刷新事件后,开始执行服务暴露。

//☆☆---ServiceBean
public void onApplicationEvent(ContextRefreshedEvent event) {
    //NOTE: 服务是否 已经暴露或者已经取消暴露
    if (!isExported() && !isUnexported()) {
        //NOTE: 暴露服务,ServiceConfig.export()
        export();
    }
}

3、 前置的参数/URL校验

校验用户配置是否正确,或者补齐默认配置,配置检查完成后,根据这些配置组装 URL。

采用 URL 作为配置信息的统一格式,所有扩展点都通过传递 URL 携带配置信息。

3.1、配置信息校验

入口:checkAndUpdateSubConfigs()

  • 检测 ProviderConfig、ApplicationConfig 等核心配置类对象是否为空,若为空,则尝试从其他配置类对象中获取相应的实例。
  • 检测并处理泛化服务和普通服务类
  • 检测本地存根配置,并进行相应的处理
  • 对 ApplicationConfig、RegistryConfig 等配置类进行检测,为空则尝试创建,若无法创建则抛出异常

3.2、解析注册中心URL

入口:loadRegistries()->parseURLs()

  • 检测是否配置注册中心,没有则返回空;
  • 构建参数映射集合
  • 构建注册中心地址列表,根据条件决定是否将其添加到 最终的注册中心结果中
    • 服务提供者 && register = true 或 null(只注册)
    • 服务消费者 && subscribe = true 或 null(只订阅)
//zk为注册中心的注册地址
zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=16576&qos.port=22222&registry=zookeeper&timestamp=1553697573153

//
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=16576&qos.port=22222&registry=zookeeper&timestamp=1553697573153

3.3、组装URL

入口:doExportUrlsFor1Protocol()

  • 配置检查完毕后,根据配置,以及其他一些信息组装 URL。URL的作用 是其为 Dubbo 配置的载体,通过 URL 可让 Dubbo 的各种配置在各个模块之间传递。
  • 代码中的呈现就是将所有的信息存放在一个map中,最后封装到一个URL对象里。

4、暴露服务

调用路径:export() -> doExportUrls -> doExportUrlsFor1Protocol()

//☆☆--ServiceConfig 基于单个协议,暴露服务
private void doExportUrls() {
    // NOTE: 支持多协议多注册中心
    // 加载注册中心地址
    List<URL> registryURLs = loadRegistries(true);
    // NOTE: 遍历protocol,向逐个注册中心分组暴露服务
    for (ProtocolConfig protocolConfig : protocols) {
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}
  • 多种协议多个注册中心,分别进行暴露
  • 通过第一步参数检查得到 URL 对象,解析scope:
    • scope != remote,导出到本地:exportLocal
    • scope != local,导出到远程:暴露服务或注册服务

服务配置暴露的 Exporter关系:

  1. {scope} 未设置时,会暴露 Local + Remote 两个,也就是 URL:Exporter = 1:2
  2. {scope} 设置为空”none“时,不会暴露,也就是 URL:Exporter = 1:0
  3. {scope} 设置为 Local 或 Remote 任一时,会暴露 Local 或 Remote 一个,也就是 URL:Exporter = 1:1

5、本地暴露

  1. 构建本地服务Dubbo URL
  2. 通过自适应的ProxyFactory创建Invoker,再通过自适应Protocol暴露服务:
    • Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local)
    • Exporter exporter = protocol.export(invoker)
  3. 调用路径
    1. 创建Invoker:ServiceConfig =>ProxyFactory$Adaptive => StubProxyFactoryWrapper => JavassistProxyFactory => Wrapper => AbstractProxyInvoker
    2. 服务暴露exporter:Protocol$Adaptive => ProtocolListenerWrapper(ProtocolFilterWrapper) => ProtocolFilterWrapper(InjvmProtocol) => InjvmProtocol => InjvmExporter => ListenerExporterWrapper
      • ProtocolFilterWrapper: 生成带Filter链的Invoker
      • ProtocolListenerWrapper:对服务暴露监听
      • InjvmProtocol:创建服务暴露的Exporter【InjvmExporter
  4. 已暴露的服务记录到List exporters中,已经暴露的URL记录到List urls中。
    (借鉴一下大佬的图)Dubbo源码通~服务暴露之本地暴露_第4张图片

5.1.1、本地服务暴露详细流程

5.1.1.1、创建Invoker

Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local)

  • 创建proxyFactory自适应实现类ProxyFactory$Adaptive,最后创建的Invoker实际类为:AbstractProxyInvoker
//☆☆--ProxyFactory
@SPI("javassist")
public interface ProxyFactory {
    /**
     * create invoker.
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}


//☆☆--ProxyFactory$Adaptive
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {

    // 创建Invoker
    //arg0:接口实现类
    //arg1:接口
    //arg2:URL对象
    public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1,  org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
        if (arg2 == null) {
            throw new IllegalArgumentException("url == null");
        }
        
        //根据URL确定调用哪一个扩展点
        org.apache.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist");
        if (extName == null) {
            throw new IllegalStateException( "Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
        }
        
        //获得扩展实现类(此处实际为StubProxyFactoryWrapper)
        org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class) .getExtension(extName);
        
        return extension.getInvoker(arg0, arg1, arg2);
    }
......
}
  • StubProxyFactoryWrapper.getInvoker(…)
//创建Invoker,proxyFactory为JavassistProxyFactory
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
    return proxyFactory.getInvoker(proxy, type, url);
}
  • JavassistProxyFactory.getInvoker(…):创建真正的Invoker
/**
* proxy:实现类
* type:接口
* url:配置载体
*/
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    // NOTE: 为目标类创建 Wrapper(动态创建)
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
    // NOTE: 创建 Invoker 类对象,并实现 doInvoke 方法。
    return new AbstractProxyInvoker<T>(proxy, type, url) {
        @Override
        protected Object doInvoke(T proxy, String methodName,
                                  Class<?>[] parameterTypes,
                                  Object[] arguments) throws Throwable {
            // NOTE:   Wrapper 的 invokeMethod 方法,在服务调用时会调用具体的方法
            return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        }
    };
}

其中Wrapper类动态创建代理类,将目标类进行封装,后续在服务调用时,调用AbstractProxyInvoker的doInvoker方法时,会将请求转发到Wrapper创建的代理类中的invokeMethod 方法。有点复杂,后面再分析

5.1.1.2、创建Exporter并暴露服务

  • Exporter exporter = protocol.export(Invoker)
  • 创建Protocol自适应实现类Protocol$Adaptive,在export()方法中调用ProtocolListenerWrapper类;
  • 在ProtocolListenerWrapper类中会调用ProtocolFilterWrapper(其中会加载一系列filter形成责任链),再在ProtocolFilterWrapper中调用InjvmProtocol创建InjvmExporter,将服务暴露(其实就是将服务封装成InjvmExporter,然后以key-value的形式将InjvmExporter对象记录在其内部的exporterMap中)
//☆☆--Protocol
@SPI("dubbo")
public interface Protocol {
   
   //没有加@Adaptive自适应注解,不会动态生成代码
    int getDefaultPort();
    
    /**
     * 服务暴露主功能入口
    */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
}

//☆☆-- Protocol$Adaptive
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(
            "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(
            "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");
        }
       //获取protocol name(本地导出为:injvm)
        org.apache.dubbo.common.URL url = arg0.getUrl();
        String extName = ((url.getProtocol() == null) ? "dubbo": url.getProtocol());

        if (extName == null) {
            throw new IllegalStateException(
                "Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" +
                url.toString() + ") use keys([protocol])");
        }

      //获得扩展类,实际得到wrapper类:ProtocolListenerWrapper,内部持有InJvmProtocol引用
        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);
    }
    ......
}

自此,服务的本地暴露,已经完成。主要流程就是将服务通过代理封装为一个Invoker,然后再有Invoker转变为Exporter的一个过程。相当于Invoker是对具体服务实现类的一个代理包裹,Invoker到Expoter,在Invoker进一步封装一些功能特性,比如:Filter、Listener等,再根据URL携带的Protocol信息,构建对应的Exporter,然后缓存在本地完成本地服务的暴露。

你可能感兴趣的:(Dubbo源码通)