1、dubbo源码分析之项目分层&核心基础技术

当我们聊dubbo的项目分层、核心基础技术的时候,我们应该考虑哪些问题?

  • dubbo的架构分层模型是什么样的(核心9层service/config层、proxy层、registry层、cluster层、monitor层、protocol层、exchange层、transport层、serializable层)?分层领域划分的依据是何?
  • dubbo以优秀的结构分层、易于扩展SPI著称,支撑它的这种扩展原理的核心技术是什么?(javassist动态编译技术、适配器类、jdk spi的特性增强、扩展点的Warraper包装器或者叫AOP、javassist 为服务提供者使用减少反射调用)

附一个dubbo项目组件模块结构图,有一个学习进攻锚点

基于框架分层&核心基础技术组件 go on !

Dubbo分层模式

dubbo官方《开发者手册》提供的分层结构模型如下图,略显复杂开始没必要关注太多细节;

  • Service层和Config层:为API接口层,是为了让Dubbo更加方便地发布服务和引用服务;服务提供方实现服务接口,使用ServiceConfig API来发布服务;服务消费方使用ReferenceConfig对服务接口进行代理;除了这两层,其他各层均为SPI层,SPI意味着下面各层都是组件化的,可被替代,这是Dubbo设计较好的一点;
  • Proxy服务代理层:该层主要是对服务消费端使用的接口进行代理,把本地调用转换为远程调用;对服务提供方实现类进行代理,把服务实现类转化为Wrapper类,这是为了减少反射调用;
  • Registry服务注册中心:服务提供者启动时注册服务到注册中心,消费方启动时去注册中心获取服务提供者的地址列表,Registry主要封装服务注册和发现逻辑;Registry扩展接口的扩展实现有ZookeeperRegistry、RedisRegistry、MulticastRegistry、DubboRegistry等。扩展接口RegistryFactory对应的接口实现有DubboRegistryFactory、RedisRegistryFactory、ZookeeperRegistryFactory;扩展接口Directory实现类有RegistryDirectory、StaticDirectory用来透明的把Invoker列表转化为一个Invoker;用户可以自定义实现各个扩展接口;
  • Cluster路由层:封装多个服务提供者的路由规则、负载均衡、集群容错的实现,并桥接服务注册中心;Cluster对应的扩展实现类有FailOverCluster(失败重试)、FailbackCluster(失败自动恢复)、FailFastCluster(快速失败)、FailsafeCluster(失败安全)、ForkingCluster(并行调度)等;负载均衡扩展接口LoadBalance对应的扩展实现类因为RandomLoandBalance(随机)、RoundRobinLoadBalance(轮询)、LeastActiveLoadBalance(最小活跃数)、ConsistentHashLoanBalance(一致性hash)等。
  • Monitor监控层:统计RPC的调用次数和调用耗时,扩展接口为MonitorFactory,对应的实现类为DobboMonitorFactory;
  • Proxy远程调用层:封装RPC调用逻辑,扩展接口为Protocol,对应的实现有RegistryProtocol、DubboProtocol、InjvmProtocol
  • Exchange信息交换层:封装请求响应模式,同步转异步,扩展接口为Exchanger,扩展实现为HeaderExchanger
  • Transport网络传输层:Mina和Netty抽象为统一接口。扩展接口Channel,对应实NettyChannel、MinaChannel等;扩展接口Transporter对应实现类有GrizzlyTransporter、MinaTransporter、NettyTransporter,Code2对应的实现类有DubboCodec、ThriftCodec等;
  • Serialize数据序列化层:提供可复用的一些工具,扩展接口Serialization,对应扩展接口实现有DubboSerialization、FastJsonSerialization、Hession2Serialization、JavaSerialization等,扩展接口ThreadPool对应的扩展实现有FixedThreadPool、CachedThreadPool、LimitedThreadPool等

分层使得Dubbo每层的功能都是可被替代的,这使得Dubbo扩展性极强;

Dubbo的核心基础技术

以Dubbo的Protocol扩展点为例:

@SPI("dubbo")
public interface Protocol{
//...
}

这里注解中的"dubbo"说明Protocol扩展接口SPI的默认实现是DubboProtocol。如果我们想自己写一个Protocol扩展接口的实现类,那么我们需要在实现类所在的jar包内的META_INFO/dubbo/目录下创建一个名字为org.apache.dubbo.rpc.Protocol的文本文件,然后配置它的内容为:

myprotocol=com.guojing.user.Myprotocol

定义Myprotocol的实现如下

package com.guojing.user;

public class MyProtocol implements Protocol{
//...
}

如何使用我们自定义的扩展点呢?在dubbo的配置模块中,扩展点均有对应的配置属性和标签,如下代码通过配置标签方式制定使用哪个扩展实现:

<dubbo:protocol name="myProtocol"/>

name与META_INFO/dubbo/目录下的org.apache.dubbo.rpc.Protocol文件中等号左侧的key的名字一致;

(1)、JDK标准SPI原理

        增强的SPI功能是从JDK标准的SPI演化而来的,所以JDK标准的SPI也很重要。JDK中的SPI是面向接口编程的,服务规则提供者在JRE的核心API里提供访问接口,具体实现由服务提供商提供;

以mysql的驱动包实现为例:rt.jar包里定义的数据库驱动接口java.sql.Driver,Mysql作为服务开发商实现该接口,会在MySQL的驱动包的META_INF/services文件夹下建立名称为java.sql.Driver的文件,文件的内容就是MySQL对java.sql.Driver接口的实现类。

通过如下代码可知,com.mysql.jdbc.Driver实现了java.sql.Driver接口:

        go on实现原理,我们知道java核心API(比如rt.jar包)是使用BootstrapClassLoader类加载器加载的,而用户提供的Jar包是由AppClassLoader加载的,如果一个类由类加载器加载,那么这个类依赖的类也是由相同的类加载器加载的。这里搜索SPI扩展实现类的API类ServiceLoader是使用BootstrapClassLoader加载的,那么ServiceLoader里面依赖的类应该也是由BootStrapClassLoader加载的,而我们已经知道加载SPI实现类的jar包是由AppClassLoader加载的,所以这是一种违反双亲委派模型的方式,线程上下文加载器ContextClassLoader就是用来解决这个问题的。

public static void main(String[] args) {
    // Driver为需要被注册、扩展的服务接口,通过静态方法ServiceLoader#load进行加载,扩展实现类
    ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);

    // 遍历服务的实现
    Iterator iterator = loader.iterator();
    while (iterator.hasNext()) {
        Driver driver = (Driver) iterator.next();
        // 打印扩展SPI实现类的类加载器
 System.out.println("driver:"+driver.getClass()+",loader:"+driver.getClass().getClassLoader());
        //打印当前线程上下文的类加载器
        System.out.println("current thread contextloader:"+Thread.currentThread().getContextClassLoader());
        //ServiceLoader 实现类的类加载器
        System.out.println("ServiceLoader loader:"+ServiceLoader.class.getClassLoader());
    }
}

执行结果如下:从结果可知找到了MYSQL的驱动,两个驱动实现类都找到了,说明了JDK标准的SPI会同时把所有实现类提前加载好实例,也可以看出ServiceLoader的加载器是BootStrap,因为输出为null,并且该类在rt.jar里面这一点也可以证明。

打开ServiceLoader类加载器的load()方法源码:这里获取了当前线程的类加载器作为扩展实现类的类加载器;

具体JDK的ServiceLoader类加载源码可以参考:JDK&Dubbo SPI实现

(2)、Dubbo增强SPI原理

Dubbo解决了JDK标准SPI的以下问题:

  • JDK标准的SPI会一次性实例化扩展点的所有实现,如果有些扩展实现初始化很耗时,但又没用上,加载很浪费资源;
  • JDK扩展点加载失败,不会友好的向用户提示的。
  • 增加了对扩展点的IoC和AOP的支持,一个扩展点可以直接使用setter()方法注入其他扩展点,也可以对扩展点使用Wrapper类进行功能增强;Wrapper可以利用javaAssist减少服务提供者反射调用的开销;

实现:dubbo为每一个扩展点接口动态生成一个适配器类,例如Protocol扩展接口对应的适配器类为Protocol$Adaptive(这个适配器类是利用javasist编码技术动态生成的,只有在debug模式下才能看到,或者利用arthas工具反编译查看 arthas使用)由这个适配器类中会使用ExtentionLoader(类似java的ServiceLoader)去加载真正的扩展实现类,ExtentionLoader的getAdaptiveExtention(XXX.Class)可以动态生成扩展接口对应的适配器类,适配器类的getExtention(String xxx)会根据扩展实现类的名称获取到扩展实现类

可以看下Protocol$Adaptive适配器类的代码:

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

/**
 * 协议自适应类
 */
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    /**
     * 服务引用
     */
    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        // 根据extName找到具体适应类,然后调用方法
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }

    /**
     * 服务暴露
     */
    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        // 根据extName找到具体适应类,然后调用方法
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }
}

核心代码段是通过URL中指定extName来加载扩展点的特定实现类:这里是DubboProtocol

com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
  • getExtensionLoder方法:dubbo的每一个扩展点接口都有自己对应的ExtentionLoader,内部通过并发Map来缓存扩展接口与对应的ExtentionLoader的映射,其中key为扩展接口的Class对象,value为对应的ExtentionLoader对应的实例;通过这个ExtentionLoader中的的getAdaptiveExtention方法首先会为扩展类生成适配器类;(主要是查看@Adaptive注解方法的适配器类)
  • getExtention方法:利用adaptive适配器类的URL中指定扩展实现类名称去获取适配器类;

这两个方法的具体源码实现参考: JDK&Dubbo SPI实现

(3)、Dubbo的动态编译生成适配器Class

        一般的java程序要想运行首先需要使用javac把源码动态编译为.class字节码文件,然后运行时使用JVM把class字节码文件加载到内存创建class对象后,使用class对象创建对象实例。而动态编译则是JVM进程运行时把源代码文件编码为字节码文件,然后使用字节码文件创建对象实例;

       Dubbo提供自己的Complier来实现动态编译,两种方式:JavassistComplier(默认实现)和JdkComplier;具体位置就是ExtensionLoader的createAdaptiveExtensionCalss()方法把扩展点源文件动态编译为Class对象的,有了Class对象后就可以使用newInstance创建某个扩展点实现对象的实例了;

/**
  * 自动生成自适应拓展的代码实现,并编译后返回该类。
  *
  * @return 类
  */
private Class<?> createAdaptiveExtensionClass() {     
// 自动生成自适应拓展的代码实现的字符串
     String code = createAdaptiveExtensionClassCode();
     // 编译代码,并返回该类
     ClassLoader classLoader = findClassLoader();
     com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
       
 return compiler.compile(code, classLoader);    
}

继续跟踪createAdaptiveExtensionClassCode(),返回的是一个字符串,这个字符串内容对于Protoco扩展点就是上面的Protocol$Adaptive适配器类内容;具体内容获取方法如下(了解关键点,无需通读):

/**
     * 自动生成自适应拓展的代码实现的字符串
     *
     * @return 代码字符串
     */
    private String createAdaptiveExtensionClassCode() {
        StringBuilder codeBuidler = new StringBuilder();
        // 遍历方法数组,判断有 @Adaptive 注解
        Method[] methods = type.getMethods();
        boolean hasAdaptiveAnnotation = false;
        for (Method m : methods) {
            if (m.isAnnotationPresent(Adaptive.class)) {
                hasAdaptiveAnnotation = true;
                break;
            }
        }
        // no need to generate adaptive class since there's no adaptive method found.
        // 完全没有Adaptive方法,则不需要生成Adaptive类
        if (!hasAdaptiveAnnotation)
            throw new IllegalStateException("No adaptive method on extension " + type.getName() + ", refuse to create the adaptive class!");

        // 生成代码:package 和 import
        codeBuidler.append("package " + type.getPackage().getName() + ";");
        codeBuidler.append("\nimport " + ExtensionLoader.class.getName() + ";");
        // 生成代码:类名
        codeBuidler.append("\npublic class " + type.getSimpleName() + "$Adaptive" + " implements " + type.getCanonicalName() + " {");

        // 循环方法
        for (Method method : methods) {
            Class<?> rt = method.getReturnType(); // 返回类型
            Class<?>[] pts = method.getParameterTypes(); // 参数类型数组
            Class<?>[] ets = method.getExceptionTypes(); // 异常类型数组

            Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
            StringBuilder code = new StringBuilder(512); // 方法体的代码
            // 非 @Adaptive 注解,生成代码:生成的方法为直接抛出异常。因为,非自适应的接口不应该被调用。
            if (adaptiveAnnotation == null) {
                code.append("throw new UnsupportedOperationException(\"method ")
                        .append(method.toString()).append(" of interface ")
                        .append(type.getName()).append(" is not adaptive method!\");");
            // @Adaptive 注解,生成方法体的代码
            } else {
                // 寻找 Dubbo URL 参数的位置
                int urlTypeIndex = -1;
                for (int i = 0; i < pts.length; ++i) {
                    if (pts[i].equals(URL.class)) {
                        urlTypeIndex = i;
                        break;
                    }
                }
                // found parameter in URL type
                // 有类型为URL的参数,生成代码:生成校验 URL 非空的代码
                if (urlTypeIndex != -1) {
                    // Null Point check
                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"url == null\");",
                            urlTypeIndex);
                    code.append(s);

                    s = String.format("\n%s url = arg%d;", URL.class.getName(), urlTypeIndex);
                    code.append(s);
                }
                // did not find parameter in URL type
                // 参数没有URL类型
                else {
                    String attribMethod = null;

                    // find URL getter method
                    // 找到参数的URL属性 。例如,Invoker 有 `#getURL()` 方法。
                    LBL_PTS:
                    for (int i = 0; i < pts.length; ++i) {
                        Method[] ms = pts[i].getMethods();
                        for (Method m : ms) {
                            String name = m.getName();
                            if ((name.startsWith("get") || name.length() > 3)
                                    && Modifier.isPublic(m.getModifiers())
                                    && !Modifier.isStatic(m.getModifiers())
                                    && m.getParameterTypes().length == 0
                                    && m.getReturnType() == URL.class) { // pubic && getting 方法
                                urlTypeIndex = i;
                                attribMethod = name;
                                break LBL_PTS;
                            }
                        }
                    }
                    // 未找到,抛出异常。
                    if (attribMethod == null) {
                        throw new IllegalStateException("fail to create adaptive class for interface " + type.getName()
                                + ": not found url parameter or url attribute in parameters of method " + method.getName());
                    }

                    // 生成代码:校验 URL 非空
                    // Null point check
                    String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"%s argument == null\");",
                            urlTypeIndex, pts[urlTypeIndex].getName());
                    code.append(s);
                    s = String.format("\nif (arg%d.%s() == null) throw new IllegalArgumentException(\"%s argument %s() == null\");",
                            urlTypeIndex, attribMethod, pts[urlTypeIndex].getName(), attribMethod);
                    code.append(s);

                    // 生成 `URL url = arg%d.%s();` 的代码
                    s = String.format("%s url = arg%d.%s();", URL.class.getName(), urlTypeIndex, attribMethod);
                    code.append(s);
                }

                String[] value = adaptiveAnnotation.value();
                // value is not set, use the value generated from class name as the key
                // 没有设置Key,则使用“扩展点接口名的点分隔 作为Key
                if (value.length == 0) {
                    char[] charArray = type.getSimpleName().toCharArray();
                    StringBuilder sb = new StringBuilder(128);
                    for (int i = 0; i < charArray.length; i++) {
                        if (Character.isUpperCase(charArray[i])) {
                            if (i != 0) {
                                sb.append(".");
                            }
                            sb.append(Character.toLowerCase(charArray[i]));
                        } else {
                            sb.append(charArray[i]);
                        }
                    }
                    value = new String[]{sb.toString()};
                }

                // 判断是否有 Invocation 参数
                boolean hasInvocation = false;
                for (int i = 0; i < pts.length; ++i) {
                    if (pts[i].getName().equals("com.alibaba.dubbo.rpc.Invocation")) {
                        // 生成代码:校验 Invocation 非空
                        // Null Point check
                        String s = String.format("\nif (arg%d == null) throw new IllegalArgumentException(\"invocation == null\");", i);
                        code.append(s);

                        // 生成代码:获得方法名
                        s = String.format("\nString methodName = arg%d.getMethodName();", i);
                        code.append(s);

                        // 标记有 Invocation 参数
                        hasInvocation = true;
                        break;
                    }
                }

                // 默认拓展名
                String defaultExtName = cachedDefaultName;
                // 获得最终拓展名的代码字符串,例如:
                // 【简单】1. url.getParameter("proxy", "javassist")
                // 【复杂】2. url.getParameter(key1, url.getParameter(key2, defaultExtName))
                String getNameCode = null;
                for (int i = value.length - 1; i >= 0; --i) { // 倒序的原因,因为是顺序获取参数,参见【复杂】2. 的例子
                    if (i == value.length - 1) {
                        if (null != defaultExtName) {
                            if (!"protocol".equals(value[i]))
                                if (hasInvocation) // 当【有】 Invocation 参数时,使用 `URL#getMethodParameter()` 方法。
                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                                else // 当【非】 Invocation 参数时,使用 `URL#getParameter()` 方法。
                                    getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
                            else // 当属性名是 "protocol" ,使用 `URL#getProtocl()` 方法获取。
                                getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
                        } else {
                            if (!"protocol".equals(value[i]))
                                if (hasInvocation)
                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName); // 此处的 defaultExtName ,可以去掉的。
                                else
                                    getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
                            else
                                getNameCode = "url.getProtocol()";
                        }
                    } else {
                        if (!"protocol".equals(value[i]))
                            if (hasInvocation)
                                getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                            else
                                getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
                        else
                            getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
                    }
                }

                // 生成代码:获取参数的代码。例如:String extName = url.getParameter("proxy", "javassist");
                code.append("\nString extName = ").append(getNameCode).append(";");
                // check extName == null?
                String s = String.format("\nif(extName == null) " +
                                "throw new IllegalStateException(\"Fail to get extension(%s) name from url(\" + url.toString() + \") use keys(%s)\");",
                        type.getName(), Arrays.toString(value));
                code.append(s);

                // 生成代码:拓展对象,调用方法。例如
                // `com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class)
                //                                                                                                           .getExtension(extName);` 。
                s = String.format("\n%s extension = (%<s)%s.getExtensionLoader(%s.class).getExtension(extName);",
                        type.getName(), ExtensionLoader.class.getSimpleName(), type.getName());
                code.append(s);

                // return statement
                if (!rt.equals(void.class)) {
                    code.append("\nreturn ");
                }

                s = String.format("extension.%s(", method.getName());
                code.append(s);
                for (int i = 0; i < pts.length; i++) {
                    if (i != 0)
                        code.append(", ");
                    code.append("arg").append(i);
                }
                code.append(");");
            }

            // 生成方法
            codeBuidler.append("\npublic " + rt.getCanonicalName() + " " + method.getName() + "(");
            for (int i = 0; i < pts.length; i++) {
                if (i > 0) {
                    codeBuidler.append(", ");
                }
                codeBuidler.append(pts[i].getCanonicalName());
                codeBuidler.append(" ");
                codeBuidler.append("arg" + i);
            }
            codeBuidler.append(")");
            if (ets.length > 0) {
                codeBuidler.append(" throws ");  // 异常
                for (int i = 0; i < ets.length; i++) {
                    if (i > 0) {
                        codeBuidler.append(", ");
                    }
                    codeBuidler.append(ets[i].getCanonicalName());
                }
            }
            codeBuidler.append(" {");
            codeBuidler.append(code.toString());
            codeBuidler.append("\n}");
        }

        // 生成类末尾的 `}`
        codeBuidler.append("\n}");

        // 调试,打印生成的代码
        if (logger.isDebugEnabled()) {
            logger.debug(codeBuidler.toString());
        }
        return codeBuidler.toString();
    }

获取到扩展点的编译内容后默认使用JavassistComplier编译器进行编译:

/**
 * Compiler. (SPI, Singleton, ThreadSafe)
 *
 * 编辑器接口
 */
@SPI("javassist")
public interface Compiler {

    /**
     * Compile java source code.
     *
     * 编译 Java 代码字符串
     *
     * @param code        Java source code
     *                    Java 代码字符串
     * @param classLoader classloader
     *                    类加载器
     * @return Compiled class
     *                    编译后的类
     */
    Class<?> compile(String code, ClassLoader classLoader);

}

具体编译方式,省略;

(4)、Dubbo扩展点的Wrapper包装器原理

        在Spring AOP中,我们可以使用多个切面对指定类的方法进行增强,在Dubbo中也提供了类似的功能,Dubbo可以指定多个包装器来对扩展点实现类的方法进行增强;

还以Protocol扩展点为例,Dubbo里使用了ProtocolFilterWrapper、ProtocolListenerWrapper等Warpper对DubboProtocol对象进行了包装增强。ProtocolFilterWrapper、ProtocolListenerWrapper这两个类都有一个拷贝构造函数,这个构造函数的参数就是扩展接口Protocol。所谓包装:

public class ProtocolListenerWrapper implements Protocol {

    private final Protocol protocol;

    /**
     * 拷贝构造函数,参数为protocol
     */
    public ProtocolListenerWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }

    public int getDefaultPort() {
        return protocol.getDefaultPort();
    }

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        // 注册中心协议
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        // 暴露服务,创建 Exporter 对象
        Exporter<T> exporter = protocol.export(invoker);
        // 获得 ExporterListener 数组
        List<ExporterListener> listeners = Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY));
        // 创建带 ExporterListener 的 Exporter 对象
        return new ListenerExporterWrapper<T>(exporter, listeners);
    }

   ...

}

其实就是在Protocol真正的服务曝露前后,这里利用拷贝构造函数在 Exporter<T> exporter = protocol.export(invoker);前后多一些额外的事情;

(5)、Dubbo使用Javassist减少反射调用开销

       Dubbo给每个服务提供实现类生成一个Wrapper类,这个Wrapper类里最终调用服务提供者的接口实现类,Wrapper类的提供可以减少反射的调用。

      没有服务提供者wrapper的情况下,当服务提供方收到消费方发来的请求后,需要根据消费者传递过来的方法名和参数反射调用服务提供者的实现类,而反射本身是有性能开销的,dubbo把每个服务提供者的实现类在编译期间通过javassist包装为一个Wrapper类,可以直接调用服务提供者实现类的具体方法,避免反射;

这个Wrapper在服务启动时在Proxy代理层的JavassistRpcProxyFactory里就已经生成了,不会在运行期影响性能;

/**
 * JavassistRpcProxyFactory
 *
 * 基于 Javassist 代理工厂实现类
 */
public class JavassistProxyFactory extends AbstractProxyFactory {

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        // TODO Wrapper类不能正确处理带$的类名
        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);
            }
        };
    }

}

参考文档:Javassist动态编译技术

发布了42 篇原创文章 · 获赞 6 · 访问量 2628

猜你喜欢

转载自blog.csdn.net/a1290123825/article/details/104822412