it技术java面试

Dubbo源码解析-能入门系列

2018-09-18  本文已影响191人  AKyS佐毅

1、ZooKeeper 可视化工具 zkui

        Register: dubbo://192.168.103.40:20880/com.of.wangpu.api.service.HelloService?anyhost=true&application=dubbo-provider&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&pid=2054&side=provider&timestamp=1535511638784, dubbo version: 2.4.10, current host: 127.0.0.1
        
        Subscribe: provider://192.168.103.40:20880/com.of.wangpu.api.service.HelloService?anyhost=true&application=dubbo-provider&category=configurators&check=false&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&pid=2054&side=provider&timestamp=1535511638784, dubbo version: 2.4.10, current host: 127.0.0.1
        2018-08-29 11:00:39.080  INFO 2054 --- 
        
        Notify urls for subscribe url provider://192.168.103.40:20880/com.of.wangpu.api.service.HelloService?anyhost=true&application=dubbo-provider&category=configurators&check=false&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&pid=2054&side=provider&timestamp=1535511638784, urls: [empty://192.168.103.40:20880/com.of.wangpu.api.service.HelloService?anyhost=true&application=dubbo-provider&category=configurators&check=false&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&pid=2054&side=provider&timestamp=1535511638784], dubbo version: 2.4.10, current host: 127.0.0.1
        2018-08-29 11:00:39.224  INFO 2054 ---
        Register: consumer://192.168.103.40/ com.of.wangpu.api.service.HelloService?application=dubbo-provider&category=consumers&check=false&connected=true&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&owner=dubbo-provider&pid=2077&side=consumer&timestamp=1535511731989, dubbo version: 2.4.10, current host: 192.168.103.40
    
        Subscribe: consumer://192.168.103.40/com.of.wangpu.api.service.HelloService?application=dubbo-provider&category=providers,configurators,routers&connected=true&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&owner=dubbo-provider&pid=2077&side=consumer&timestamp=1535511731989, dubbo version: 2.4.10, current host: 192.168.103.40
    
     Notify urls for subscribe url consumer://192.168.103.40/com.of.wangpu.api.service.HelloService?application=dubbo-provider&category=providers,configurators,routers&connected=true&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&owner=dubbo-provider&pid=2077&side=consumer&timestamp=1535511731989, urls: [dubbo://192.168.103.40:20880/com.of.wangpu.api.service.HelloService?anyhost=true&application=dubbo-provider&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&pid=2054&side=provider&timestamp=1535511638784, empty://192.168.103.40/com.of.wangpu.api.service.HelloService?application=dubbo-provider&category=configurators&connected=true&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&owner=dubbo-provider&pid=2077&side=consumer&timestamp=1535511731989, empty://192.168.103.40/com.of.wangpu.api.service.HelloService?application=dubbo-provider&category=routers&connected=true&dubbo=2.4.10&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&owner=dubbo-provider&pid=2077&side=consumer&timestamp=1535511731989], dubbo version: 2.4.10, current host: 192.168.103.40
    
      Successed connect to server /192.168.103.40:20880 from NettyClient 192.168.103.40 using dubbo version 2.4.10, channel is NettyChannel [channel=[id: 0x27f1bbe0, /192.168.103.40:53619 => /192.168.103.40:20880]], dubbo version: 2.4.10, current host: 192.168.103.40
    
      Start NettyClient zuoyideMacBook-Pro.local/192.168.103.40 connect to the server /192.168.103.40:20880, dubbo version: 2.4.10, current host: 192.168.103.40
      
        Refer dubbo service com.of.wangpu.api.service.HelloService from url zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=dubbo-provider&check=false&connected=true&dubbo=2.4.10&inside.invoker.count=1&inside.invokers=dubbo%3A%2F%2F192.168.103.40%3A20880%2Fcom.of.wangpu.api.service.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-provider%26dubbo%3D2.4.10%26interface%3Dcom.of.wangpu.api.service.HelloService%26methods%3DsayHello%26pid%3D2054%26side%3Dprovider%26timestamp%3D1535511638784&interface=com.of.wangpu.api.service.HelloService&methods=sayHello&owner=dubbo-provider&pid=2077&side=consumer&timestamp=1535511731989, dubbo version: 2.4.10, current host: 192.168.103.40

2、Dubbo相关知识点总结

image

3、SPI设计目标

4、dubbo自己的SPI实现

image
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    if (type == null)//拓展点类型非空判断
        throw new IllegalArgumentException("Extension type == null");
    if(!type.isInterface()) {//拓展点类型只能是接口
        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    }
    if(!withExtensionAnnotation(type)) {//需要添加spi注解,否则抛异常
        throw new IllegalArgumentException("Extension type(" + type + 
                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    }
    //从缓存EXTENSION_LOADERS中获取,如果不存在则新建后加入缓存
    //对于每一个拓展,都会有且只有一个ExtensionLoader与其对应
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

private ExtensionLoader(Class<?> type) {
    this.type = type;
    //这里会存在递归调用,ExtensionFactory的objectFactory为null,其他的均为AdaptiveExtensionFactory
    //AdaptiveExtensionFactory的factories中有SpiExtensionFactory,SpringExtensionFactory
    //getAdaptiveExtension()这个是获取一个拓展装饰类对象.细节在下篇讲解
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

其次我们从Dubbo的启动开始分析

ExtensionLoader.getExtensionLoader(Container.class);
    -->this.type = type;
    -->objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
       -->ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())
           -->this.type = type;
           -->objectFactory = null;
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();

objectFactory作用,为dubbo的IOC提供所有对象。

继续看还有个很重要的类Holder,这个类用于保存一个值,并且给值添加volatile来保证线程的可见性.

image

5、SPI机制的adpative原理 getAdaptiveExtension

image image
ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();//为cachedAdaptiveInstance赋值
   --->createAdaptiveExtension()
       --->getAdaptiveExtensionClass()
           --->getExtensionClasses() //为cachedClasses对象赋值。
             --->loadFile()   //加载配置文件
           --->createAdaptiveExtensionClass。//自动生成和编译一个动态Adaptive类,这是一个代理类。
               --->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();//获取代码编译器
               --->ompiler.compile(code, classLoader);//加载代理

主要做了以下几件事情:

image

此时回忆一下Protocol结构

 /**
     * 暴露远程服务:<br>
     * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
     * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
     * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
     *
     * @param <T>     服务的类型
     * @param invoker 服务的执行体
     * @return exporter 暴露服务的引用,用于取消暴露
     * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    /**
     * 引用远程服务:<br>
     * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>
     * 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br>
     * 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br>
     *
     * @param <T>  服务的类型
     * @param type 服务的类型
     * @param url  远程服务的URL地址
     * @return invoker 服务的本地代理
     * @throws RpcException 当连接服务提供方失败时抛出
     */
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

    /**
     * 释放协议:<br>
     * 1. 取消该协议所有已经暴露和引用的服务。<br>
     * 2. 释放协议所占用的所有资源,比如连接和端口。<br>
     * 3. 协议在释放后,依然能暴露和引用新的服务。<br>
     */

动态生成的协议代理类

    public class Protocol$Adpative 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.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();
 
            //默认选择dubbo协议,否则根据url中带的协议属性来选择对应的协议处理对象,这样可以动态选择不同的协议
            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])");
 
            //根据拿到的协议key从缓存的map中取协议对象
            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);
        }
        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])");
 
            //根据拿到的协议key从缓存的map中取协议对象
            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);
        }
    }

到这里接口的代理已经生成啦!再回退到createAdaptiveExtension方法中。

injectExtension((T) getAdaptiveExtensionClass().newInstance());

作用:

6、dubbo自己的IOC和AOP原理

getExtension(String name)

getExtension(String name) //指定对象缓存在cachedInstances;get出来的对象wrapper对象,例如protocol就是ProtocolFilterWrapper和ProtocolListenerWrapper其中一个。
  -->createExtension(String name)
    -->getExtensionClasses()
    -->injectExtension(T instance)//dubbo的IOC反转控制,就是从spi和spring里面提取对象赋值。
      -->objectFactory.getExtension(pt, property)
        -->SpiExtensionFactory.getExtension(type, name)
          -->ExtensionLoader.getExtensionLoader(type)
          -->loader.getAdaptiveExtension()
        -->SpringExtensionFactory.getExtension(type, name)
          -->context.getBean(name)
    -->injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))//AOP的简单设计

7、dubbo的动态编译

上文我们讲到编译类,那么Dubbo是怎么实现的呢

com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();//获取代码编译器
        return compiler.compile(code, classLoader);//加载代理
image

第一步


image

第二步


image
第三步
image

第四步


image
第五步
image

在看JavassistCompiler 编译类之前,首先需要移动的javac基础知识。下边我们看一个例子

public class CompilerByJavassist {

    public static void main(String[] args) throws Exception {

        // ClassPool:CtClass对象的容器
        ClassPool pool = ClassPool.getDefault();

        // 通过ClassPool生成一个public新类Emp.java
        CtClass ctClass = pool.makeClass("com.study.javassist.Emp");

        // 添加属性
        // 首先添加属性private String ename
        CtField enameField = new CtField(pool.getCtClass("java.lang.String"),
                "ename", ctClass);
        enameField.setModifiers(Modifier.PRIVATE);
        ctClass.addField(enameField);

        // 其次添加熟悉privtae int eno
        CtField enoField = new CtField(pool.getCtClass("int"), "eno", ctClass);
        enoField.setModifiers(Modifier.PRIVATE);
        ctClass.addField(enoField);

        // 为属性ename和eno添加getXXX和setXXX方法
        ctClass.addMethod(CtNewMethod.getter("getEname", enameField));
        ctClass.addMethod(CtNewMethod.setter("setEname", enameField));
        ctClass.addMethod(CtNewMethod.getter("getEno", enoField));
        ctClass.addMethod(CtNewMethod.setter("setEno", enoField));

        // 添加构造函数
        CtConstructor ctConstructor = new CtConstructor(new CtClass[] {},
                ctClass);
        // 为构造函数设置函数体
        StringBuffer buffer = new StringBuffer();
        buffer.append("{\n").append("ename=\"yy\";\n").append("eno=001;\n}");
        ctConstructor.setBody(buffer.toString());
        // 把构造函数添加到新的类中
        ctClass.addConstructor(ctConstructor);

        // 添加自定义方法
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "printInfo",
                new CtClass[] {}, ctClass);
        // 为自定义方法设置修饰符
        ctMethod.setModifiers(Modifier.PUBLIC);
        // 为自定义方法设置函数体
        StringBuffer buffer2 = new StringBuffer();
        buffer2.append("{\nSystem.out.println(\"begin!\");\n")
                .append("System.out.println(ename);\n")
                .append("System.out.println(eno);\n")
                .append("System.out.println(\"over!\");\n").append("}");
        ctMethod.setBody(buffer2.toString());
        ctClass.addMethod(ctMethod);

        //最好生成一个class
        Class<?> clazz = ctClass.toClass();
        Object obj = clazz.newInstance();
        //反射 执行方法
        obj.getClass().getMethod("printInfo", new Class[] {})
                .invoke(obj, new Object[] {});

        // 把生成的class文件写入文件
        byte[] byteArr = ctClass.toBytecode();
        FileOutputStream fos = new FileOutputStream(new File("D://Emp.class"));
        fos.write(byteArr);
        fos.close();
    }
}

此时就不难理解上边第五步的代码了。

上一篇 下一篇

猜你喜欢

热点阅读