精华内容
下载资源
问答
  • 对Dobbo中的动态生成类进行了描述,包括:(1)自适应机制中的Adaptive动态类、(2)服务引用的proxy接口代理类、(3)Wrapper包装类


    Dubbo中的动态生成类体现在如下几个方面:1. 自适应扩展机制中的Adaptive类;2. 服务引用的接口代理类;3. 方便获取类信息的Wrapper。他们具有不同的作用,下面逐一分析。

    自适应扩展机制中的Adaptive类

    自适应扩展机制是Dubbo对SPI机制(Service Provider Interface)的增强实现版,和API相比,在使用SPI时,调用方无需指定接口的具体实现,而在程序运行时决定。

    自适应扩展机制说明

    和原生SPI一样,在Dubbo的自适应扩展机制中,用META-INF目录下的配置文件指定接口实现类,配置文件名为接口的全限定类名。如下图,在dubbo-cluster包下的META-INF目录下,配置文件com.alibaba.dubbo.rpc.cluster.LoadBalance指定了四个接口实现,等号左边是实现类的name,等号右边是实现类的全限定类名。

    // dubbo-cluster包中的配置文件com.alibaba.dubbo.rpc.cluster.LoadBalance
    random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
    roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
    leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
    consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
    

    在Dubbo中,自适应扩展机制主要由类ExtensionLoader实现,对于接口T(本例中的LoadBalance),ExtensionLoader提供getAdaptiveExtension()方法,返回接口T的自适应实现类实例,该类实现了接口T,作为所有其它接口T实现类的入口。在对接口T中注解了@Adaptive的方法进行调用时,自适应实现类实例根据调用方法的参数获取实现类的name,并调用getExtension(String name)获取相应类实例,使用相应方法完成调用。
    在上面的例子中,调用通过自适应类的接口方法select时,如果参数url中关于负载均衡的参数使用了"roundrobin",那么自适应实现类对象会获取到RoundRobinLoadBalance的实例,并通过该实例调用select方法,实现特定的负载均衡策略。

    @SPI(RandomLoadBalance.NAME)
    public interface LoadBalance {
        @Adaptive("loadbalance")
        <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
    }
    

    Adaptive类实现自适应

    如上所述,Dubbo中的自适应扩展机制是通过ExtensionLoader<T>类实现的,其中,T是SPI接口,ExtensionLoader<T>提供getAdaptiveExtension()方法,该方法返回一个自适应实现类的实例。这个自适应类可以是预先定义的,也可以是动态生成的。这里先认为这个自适应类是动态生成的,这符合大部分的场景,预定义实现类的情况将在下一节会详细描述。把这个动态生成的自适应类记为T$Adaptive,它是接口T的一个实现,作为接口T所有其它实现类的入口,完成了根据运行时动态传递的参数决定采用不同实现类实例的功能,是Dubbo实现SPI的关键点。下面举例说明

    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])");
            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])");
            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);
        }
    }	
    

    上面给出了一个T$Adaptive类的例子,该例中接口T为Protocol,Protocol$Adaptive实现了export和refer两个接口方法,实现的思路都是从export和refer的接口参数中获取Dubbo URL,然后根据URL中动态传递的protocol参数确定应用的Protocol实现类name,通过ExtensionLoader<T>的getExtension(String name)方法获取该实现类的实例,调用具体实现类的export和refer方法。下面贴出了Protocol接口的定义代码,其中,注解@SPI(“dubbo”)表示该接口为一个SPI,并且默认实现类名称为dubbo;export和refer上的注解@Adaptive表示在增强类T$Adaptive的代码中实现这两个方法,没有注解@Adaptive的方法就不实现。

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

    ExtensionLoader<T>的getExtension(String name)方法代码贴出如下,ExtensionLoader<T>先到缓存cachedInstances中看有没有name对应类的实例,有就直接返回,没有则调用createExtension(String name)创建一个。

        // ExtensionLoader
        public T getExtension(String name) {
            ...
            // 从缓存取, cachedInstances的key是类对应的name,value是实例
            Holder<Object> holder = cachedInstances.get(name);
            if (holder == null) {
                cachedInstances.putIfAbsent(name, new Holder<Object>());
                holder = cachedInstances.get(name);
            }
            Object instance = holder.get();
            if (instance == null) {
                synchronized (holder) {
                    instance = holder.get();
                    if (instance == null) {
                        // 缓存没有创建一个
                        instance = createExtension(name);
                        holder.set(instance);
                    }
                }
            }
            return (T) instance;
        }
    

    createExtension(String name)代码如下,首先调用getExtensionClasses().get(name)获取名为name的类定义,然后以类定义为key从缓存EXTENSION_INSTANCES中取实例,有则返回,没有则创建一个,并用包裹类进行包裹。

        // ExtensionLoader
        private T createExtension(String name) {
            // 取类定义
            Class<?> clazz = getExtensionClasses().get(name);
            if (clazz == null) {
                throw findException(name);
            }
            try {
                // 从缓存取, EXTENSION_INSTANCES的key是类定义,value是实例
                // EXTENSION_INSTANCES 和 cachedInstances 都是实例的缓存,但是key不一样
                // dubbo的spi配置文件中,多个name可能对应同一个实现类的定义
                T instance = (T) EXTENSION_INSTANCES.get(clazz);
                if (instance == null) {
                    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                    instance = (T) EXTENSION_INSTANCES.get(clazz);
                }
                injectExtension(instance);
                // 用包裹类嵌套
                Set<Class<?>> wrapperClasses = cachedWrapperClasses;
                if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
                    for (Class<?> wrapperClass : wrapperClasses) {
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                    }
                }
                return instance;
            } catch (Throwable t) {
                throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                        type + ")  could not be instantiated: " + t.getMessage(), t);
            }
        }
    

    在getExtensionClasses中,根据指定的接口类型,扫描"META-INF/services/"、“META-INF/dubbo/”、"META-INF/dubbo/internal/"三个目录下关于该接口的实现类,加载到ExtensionLoader的成员cachedClasses中并返回,代码片段如下

        // ExtensionLoader
        private Map<String, Class<?>> getExtensionClasses() {
            Map<String, Class<?>> classes = cachedClasses.get();
            if (classes == null) {
                synchronized (cachedClasses) {
                    classes = cachedClasses.get();
                    if (classes == null) {
                        classes = loadExtensionClasses();
                        // 加载到cachedClasses
                        cachedClasses.set(classes);
                    }
                }
            }
            return classes;
        }
    
        // ExtensionLoader
        private Map<String, Class<?>> loadExtensionClasses() {
            ......
            Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
            // 扫描META-INF/dubbo/internal/
            loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
            // 扫描META-INF/dubbo/
            loadDirectory(extensionClasses, DUBBO_DIRECTORY);
            // 扫描META-INF/services/
            loadDirectory(extensionClasses, SERVICES_DIRECTORY);
            return extensionClasses;
        }
    

    综上所述,对于接口T,ExtensionLoader提供getAdaptiveExtension()方法,返回动态生成类T$Adaptive的实例,T$Adaptive实现了接口T,作为所有其它接口T实现类的入口,在对接口T中注解了@Adaptive的方法进行调用时,T$Adaptive的实现根据参数调用getExtension (String name)获取相应类的实例,调用相应实现方法完成调用。

    SPI实现类的类型

    Dubbo的自适应扩展机制中,把SPI的实现类分为三类:

    • 自适应实现类:如果接口T有自适实现类的实现,那么上述ExtensionLoader<T>的getAdaptiveExtension()方法直接返回自适应类的实例,而不会动态生成T$Adaptive类。通过Dubbo中定义的注解,可以表示自适应类,例如,接口ExtensionFactory实现AdaptiveExtensionFactory具有注解@Adapitve,它是一个自适应类。
    @Adaptive
    public class AdaptiveExtensionFactory implements ExtensionFactory {
          ...
    }
    
    • 包裹类:在上述的ExtensionLoader<T>的getExtension(String name)方法中,生成name对应实现类的实例时(注意这个name不是类名,而是SPI配置文件中的key),会用包裹类逐一包裹,最后得到的实际上是经过层层包裹的实例。包裹类是通过是否具有构造函数来判断的,例如,Protocol的实现ProtocolListenerWrapper是一个包裹类,它具有构造函数,因此是一个包裹类。ProtocolListenerWrapper的内部对象protocol是用来存放被包裹的对象的;ProtocolListenerWrapper实现了接口方法export和refer,并进行了增强实现,在本例中ProtocolListenerWrapper进行了增加监听器的增强实现。
    public class ProtocolListenerWrapper implements Protocol {
        private final Protocol protocol;
        public ProtocolListenerWrapper(Protocol protocol) {
            if (protocol == null) {
                throw new IllegalArgumentException("protocol == null");
            }
            this.protocol = protocol;
        }
        
        @Override
        public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
                return protocol.export(invoker);
            }
            return new ListenerExporterWrapper<T>(protocol.export(invoker),
                    Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                            .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
        }
    
        @Override
        public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
            if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                return protocol.refer(type, url);
            }
            return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
                    Collections.unmodifiableList(
                            ExtensionLoader.getExtensionLoader(InvokerListener.class)
                                    .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
        }
    
    • 普通实现类:接口T的一般实现类,是功能实现的基本内容。一般使用ExtensionLoader的getExtension(name)方法获取实现类实例。普通类型的实现类可以用@Activate注解进行标注。@Activate注解指定两个必须的值,group和value。ExtensionLoader提供方法getActivateExtension获取多个标注了@Activate的实现类实例。该方法如下:group传入要匹配的组,和实现类@Activate注解里的group匹配,如果匹配上,且url里的参数parameters的key包含@Activate注解的value值(类似于parameters.keyset().contains(value)的意思),则返回列表中包含该实现类;根据key在url的parameters参数取值(parameters.get(key)),根据取值获取希望返回的实现类name列表,根据这些name调用ExtensionLoader的getExtension(name)方法获取实现类实例。
        public List<T> getActivateExtension(URL url, String key, String group) {
            String value = url.getParameter(key);
            return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
        }
    

    在上面三种类型的实现类中:(1)在无法以固定模式动态生成接口T的自适应类T$Adaptive时,需要实现自适应实现类;(2)Wrap包裹类用于对接口实现类的功能进行增强,并不会通过getExtension(String name)接口直接获取;(3)普通实现类是基本功能的实现类,通过getExtension(String name)接口直接获取获取其实例,其中关于@Activate注解的用法可以参看Filter接口以及其实现类。

    重要数据结构

    public class ExtensionLoader<T> {
        // 接口T的类型
        private final Class<?> type;
        // 包裹类缓存,只保存包裹类型的实现类
        private Set<Class<?>> cachedWrapperClasses;
        // 自适应类缓存,只保存自适应类型的实现类,最多只能有一个
        private volatile Class<?> cachedAdaptiveClass = null;      
        // 自适应类实例缓存,只保存自适应类型的实现类实例,最多只能有一个
        private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
        // 实现类名称缓存,key为实现类的类型,value为实现类的name,只保存普通类型的实现类名称
        private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();               
        // 实现类缓存,key为实现类的name,value为实现类,只保存普通类型的实现类
        private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();     
        // cachedInstances是实现类实例缓存,key为实现类的名称,value为实例,只有普通类型的实现类才会产生实例
        // EXTENSION_INSTANCES是实现类实例缓存,key为实现类的类型,value为实例,只有普通类型的实现类才会产生实例
        // cachedInstances和EXTENSION_INSTANCES相比,因为多个name可能对应到同一个实现类,所以cachedInstances应该比EXTENSION_INSTANCES多
        // cachedInstances中的多个value可能在EXTENSION_INSTANCES中是同一个value
        private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();        
        private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
        // Activate注解缓存
        private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
        // 默认实现类名称,只能是普通类型的实现类
        private String cachedDefaultName;    
               
    }
    

    服务引用中的接口代理类

    服务引用的实现

    Dubbo对服务的引用是通过接口代理类实现的。

    <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
    

    例如,按照上面xml进行配置,Dubbo会动态生成一个DemoService的接口代理类proxy0,该代理类的成员handler完成对所引用的实际服务的调用,包括本地调用和远程调用。同时,该代理类proxy0还实现了其它一些扩展接口,这些接口的也都通过handler的invoke方法实现。

    public class proxy0 implements ClassGenerator.DC, EchoService, DemoService {
        public static Method[] methods;
        private InvocationHandler handler;
        public proxy0(InvocationHandler invocationHandler) {
            this.handler = invocationHandler;
        }
        public proxy0() {
        }
        public String sayHello(String string) {
            // 将参数存储到 Object 数组中
            Object[] arrobject = new Object[]{string};
            // 调用 InvocationHandler 实现类的 invoke 方法得到调用结果
            Object object = this.handler.invoke(this, methods[0], arrobject);
            // 返回调用结果
            return (String)object;
        }
        /** 回声测试方法 */
        public Object $echo(Object object) {
            Object[] arrobject = new Object[]{object};
            Object object2 = this.handler.invoke(this, methods[1], arrobject);
            return object2;
        }
    }
    

    如下给出了InvocationHandler的完整定义,InvocationHandler中包含一个成员invoker,invoker是对最终调用接口DemoService的层层封装,通过这些封装,可以完成额外的工作,包括上述扩展接口的功能。

    public class InvokerInvocationHandler implements InvocationHandler {
        private final Invoker<?> invoker;
        public InvokerInvocationHandler(Invoker<?> handler) {
            this.invoker = handler;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ...
            return invoker.invoke(new RpcInvocation(method, args)).recreate();
        }
    }
    

    动态生成接口代理类的过程

    下面贴出了生成接口代理类的代码

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

    首先看一下Proxy,Proxy的代码贴出如下,Proxy是一个抽象类:它有一个抽象方法newInstance,和一个静态方法getProxy。getProxy实际返回一个Proxy子类的实例,该子类实现了抽象方法newInstance。因此,上面JavassistProxyFactory中生成接口代理类实例的getProxy方法可以看做分为两步实现:第一步调用Proxy.getProxy(interfaces)得到一个Proxy子类的实例,这个Proxy子类是动态生成的;第二步调用子类实例的newInstance方法得到接口代理类实例,这个接口代理类也是动态生成的。

    // Proxy 
    public abstract class Proxy {
        ...
        public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {...}
        ...
        abstract public Object newInstance(InvocationHandler handler);
        ... 
    }
    

    重要数据结构

    public abstract class Proxy {
        ...
        // Proxy的子类缓存
        // 第一级Map的key是类加载器
        // 第二级Map的key是接口字符串,例如"com.alibaba.dubbo.demo.DemoService;com.alibaba.dubbo.rpc.service.EchoService;"
        // 第二级Map的可以理解为,可以生成某些接口代理类的Proxy子类
        private static final Map<ClassLoader, Map<String, Object>> ProxyCacheMap = new WeakHashMap<ClassLoader, Map<String, Object>>();
        ...
    }
    
    

    方便获取类信息的Wrapper

    抽象Wrapper类提供静态方法makeWrapper,该静态方法返回一个Wrapper子类的实例,该子类是动态生成的,它实现了Wrapper的抽象方法,通过这些抽象方法,可以方便获取类c的各种属性,也可以对c的方法进行反射调用。

    // Wrapper
    private static Wrapper makeWrapper(Class<?> c){
    ...
    }
    

    下面是Wrapper的抽象方法

    public abstract class Wrapper {
        abstract public String[] getPropertyNames();
        abstract public Class<?> getPropertyType(String pn);
        abstract public boolean hasProperty(String name);
        abstract public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException, IllegalArgumentException;
        abstract public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException, IllegalArgumentException;
        abstract public String[] getMethodNames();
        abstract public String[] getDeclaredMethodNames();
        abstract public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException, InvocationTargetException;
    }
    

    版本

    本文参考Dubbo2.6.8版本

    展开全文
  • dubbo服务运行过程中,上传正确的java代码文件,自动编译生成class并注册到dubbo zk 中,可以正常dubbo invoke来调用。整个过程服务不用重启。我写了个框架并命名为D-Unit。
  • 接入 YApi 统一管理 随着 Dubbo 服务越来越多,我们需要一个 API 平台能够查看到所有的 Dubbo 服务的 RPC API 接口信息。目前,艿艿比较推荐和使用的 YApi 平台,主要原因是 YApi 可以采集 Swagger 提供的接口元...

    点击上方“芋道源码”,选择“设为星标

    管她前浪,还是后浪?

    能浪的浪,才是好浪!

    每天 8:55 更新文章,每天掉亿点点头发...

    源码精品专栏

     
    • 1. 概述

    • 2. Swagger Dubbo

    • 3. 接入 Knife4j 作为 Swagger UI 界面

    • 4. 接入 YApi 统一管理

    • 666. 彩蛋


    1. 概述

    在使用 SpringMVC 构建 HTTP API 时,我们可以通过 Swagger 自动生成 HTTP 接口文档,通过 Swagger UI 界面上进行 HTTP 接口调试。如下图所示:

    Swagger HTTP 界面

    秃头提示:对 Swagger 不太了解的胖友,可以去阅读下艿艿写的 《芋道 Spring Boot API 接口文档 Swagger 入门》 文章。

    开发体验杠杠的好!但是在使用 Dubbo 构建 RPC API 时,简直想要自闭。常常面临的痛苦是:

    • 隔壁团队的老王,不肯给 Dubbo 写接口文档,只能从他的 Dubbo API 的 jar 包中寻寻觅觅想要的接口,贼不方便~

    • 自己编写的每个 Dubbo API 接口,都需要写个 Controller 或者 Test 类去调用测试,接口调试非常麻烦~

    在一个逐步自闭到要爆炸的下午,艿艿做了一波 Dubbo 接口文档与接口调试的调研,想要尝试这块的痛点。结果比想象中顺利且简单,这不趁着这无聊到抠脚的周末,整理下分享给胖友。

    IDEA 上号

    ???? 这么良心,不考虑给艿艿点个赞么?!

    芳芳都说好!!!

    2. Swagger Dubbo

    哈哈哈~实际上,我们还是可以通过 Swagger 实现 Dubbo 的接口文档接口调试这两个功能。效果如下图:

    Swagger Dubbo 界面

    Swagger 对 Dubbo 的支持,是通过 swagger-dubbo 项目所实现,其 Github 地址是 https://github.com/Sayi/swagger-dubbo。

    Swagger Dubbo 项目

    2.1 小小改造

    dubbo-swagger 最新版本 v2.0.1,并不支持 Dubbo 2.7.0 开始的版本,因此我们需要做一点点小小的改造。具体的改造点如下:

    良心艿:怕麻烦的胖友,可以看艿艿 fork 出来的仓库 https://github.com/YunaiV/swagger-dubbo,给改的明明白白了,直接能用。

    1. 合并 PR#50 的代码,支持 Dubbo 2.7.0 开始的版本。

      1. 合并 PR#46 的代码,使 Swagger UI 界面正确展示 POJO 类型的参数。

      2.2 快速入门

      swagger-dubbo 项目提供了 Spring Boot + Dubbo 的示例 dubbo-provider-springboot,我们来一起看一看。

      dubbo-provider-springboot 示例

      不过 dubbo-provider-springboot 示例暂时有一点点小“问题”,我们需要稍微修正下。

      良心艿:怕麻烦的胖友,可以直接看艿艿修改后的示例地址 https://github.com/YunaiV/swagger-dubbo/blob/master/swagger-dubbo/。

      2.2.1 修改依赖

      示例使用 Dubbo 的版本是 2.6.0,而我们希望使用 Dubbo 的版本是 2.7.0 开始,因此需要略微修改 pom.xml 如下:

      <!-- 去除 Dubbo 2.6.0 的依赖 -->
      <!--  <dependency>-->
      <!--   <groupId>com.alibaba</groupId>-->
      <!--   <artifactId>dubbo</artifactId>-->
      <!--   <version>2.6.0</version>-->
      <!--   <exclusions>-->
      <!--    <exclusion>-->
      <!--     <groupId>org.springframework</groupId>-->
      <!--     <artifactId>spring</artifactId>-->
      <!--    </exclusion>-->
      <!--   </exclusions>-->
      <!--  </dependency>-->
      <!--  <dependency>-->
      <!--   <groupId>org.apache.zookeeper</groupId>-->
      <!--   <artifactId>zookeeper</artifactId>-->
      <!--   <version>3.5.2-alpha</version>-->
      <!--  </dependency>-->
      <!--  <dependency>-->
      <!--      <groupId>org.apache.curator</groupId>-->
      <!--      <artifactId>curator-framework</artifactId>-->
      <!--      <version>4.0.1</version>-->
      <!--  </dependency>-->
      
      <!-- 引入 Dubbo 2.7.0 的依赖 -->
      <dependency>
          <groupId>org.apache.dubbo</groupId>
          <artifactId>dubbo</artifactId>
          <version>2.7.4.1</version>
      </dependency>
      <dependency>
          <groupId>org.apache.dubbo</groupId>
          <artifactId>dubbo-spring-boot-starter</artifactId>
          <version>2.7.4.1</version>
      </dependency>
      <!-- 使用 Zookeeper 作为注册中心 -->
      <dependency>
          <groupId>org.apache.curator</groupId>
          <artifactId>curator-framework</artifactId>
          <version>2.13.0</version>
      </dependency>
      <dependency>
          <groupId>org.apache.curator</groupId>
          <artifactId>curator-recipes</artifactId>
          <version>2.13.0</version>
      </dependency>
      <dependency>
          <groupId>org.apache.curator</groupId>
          <artifactId>curator-client</artifactId>
          <version>4.0.1</version>
      </dependency>
      

      2.2.2 修改配置文件

      修改 application.properties 配置文件,增加如下配置项:

      swagger.dubbo.application.groupId=com.deepoove
      swagger.dubbo.application.artifactId=dubbo-provider-springboot
      swagger.dubbo.application.version=2.0.2-SNAPSHOT
      

      通过 swagger.dubbo.application 配置项,可以知道该 Dubbo 服务的 API jar 包的 groupIdartifactIdversion 信息。

      友情提示:swagger-dubbo 还提供了其它配置项,一般默认即可。感兴趣的胖友,可以看看 SwaggerDubboProperties 配置类。

      2.2.3 简单测试

      下面,我们来运行下 dubbo-provider-springboot 示例项目,感受下 swagger-dubbo 的具体功能。

      第一步,本地运行一个 ZooKeeper 服务,作为注册中心。

      第二步,执行 Application 类,将 Dubbo 服务提供者进行启动。启动成功的日志如下:

      14:12:24.180 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8077 (http) with context path ''
      14:12:24.184 [main] INFO  c.d.d.p.springboot.Application - Started Application in 3.443 seconds (JVM running for 4.109)
      

      第三步,使用浏览器访问 http://127.0.0.1:8077/distv2/index.html 地址,进入 swagger-dubbo 提供的 Swagger UI 界面,如下图所示:

      Swagger Dubbo 接口文档

      这里,我们已经可以看到当前 Dubbo 服务所提供的 RPC 接口文档

      友情提示:访问的 Swagger UI 界面,就是我们在示例项目的 resources/static/distv2 目录下的静态资源。

      第四步,我们可以任一选择一个 Dubbo API 接口,填写参数后,点击「Try it out!」按钮进行 RPC 接口调试。如下图所示:

      Swagger Dubbo 接口调试

      通过接口调试的功能,我们可以方便的测试。

      友情提示:可能有胖友会好奇,swagger-dubbo 是如何实现接口调试的功能的呢?答案可以到 DubboHttpController 类中去寻找。

      简单来说,就是使用 Dubbo 提供的 Java API 获得到对应 Dubbo Consumer 对象,将 HTTP 请求参数映射成 Dubbo RPC 请求参数,最终进行调用。

      3. 接入 Knife4j 作为 Swagger UI 界面

      由于 dubbo-swagger 项目并未将其提供的 Swagger UI 界面封装成一个 jar 包,导致集成 dubbo-swagger 的 Dubbo 项目需要在其 resources 目录下,添加相应的 Swagger UI 的静态资源。例如说:

      Dubbo Swagger UI 界面的静态资源

      显然,这么做是非常不优雅的,因为后续无法方便的更新 Swagger UI 的静态资源。那么,应该怎么办呢?这里艿艿先演示一种解决方案,也是目前团队所采用的,接入 Knife4j 作为 Swagger UI 界面。

      秃头提示:Knife4j 是基于 Swagger 的增强解决方案,提供更强大的 Swagger 的功能,以及更易用的 Swagger UI 界面。

      下面,我们继续在 dubbo-provider-springboot 示例项目上,进行改造接入。

      3.1 修改依赖

      修改 pom.xml 文件,引入 Knife4j Starter 依赖。具体如下:

      <dependency>
          <groupId>com.github.xiaoymin</groupId>
          <artifactId>knife4j-spring-boot-starter</artifactId>
          <version>2.0.5</version>
      </dependency>
      

      3.2 创建 SwaggerConfiguration

      创建 SwaggerConfiguration 配置类,自定义 SwaggerResourcesProvider Bean。代码如下:

      @Configuration
      @EnableSwagger2 // 标记项目启用 Swagger API 接口文档
      public class SwaggerConfiguration {
      
          @Bean
          @Primary
          public SwaggerResourcesProvider newSwaggerResourcesProvider(Environment env, DocumentationCache documentationCache) {
              return new InMemorySwaggerResourcesProvider(env, documentationCache) {
      
                  @Override
                  public List<SwaggerResource> get() {
                      // 1. 调用 InMemorySwaggerResourcesProvider
                      List<SwaggerResource> resources = super.get();
                      // 2. 添加 swagger-dubbo 的资源地址
                      SwaggerResource dubboSwaggerResource = new SwaggerResource();
                      dubboSwaggerResource.setName("dubbo");
                      dubboSwaggerResource.setSwaggerVersion("2.0");
                      dubboSwaggerResource.setUrl("/swagger-dubbo/api-docs");
                      dubboSwaggerResource.setLocation("/swagger-dubbo/api-docs"); // 即将废弃,和 url 属性等价。
                      resources.add(0, dubboSwaggerResource);
                      return resources;
                  }
      
              };
          }
      
      }
      

      艿艿先暂时不解释为什么要这么做,因为涉及到 Swagger 运行机制相关的知识,略微有点小复杂~~稍后,在「3.4 再看 SwaggerConfiguration」小节,我们在一起遨游这块知识的海洋。

      不想学习

      3.3 简单测试

      下面,我们重新运行下 dubbo-provider-springboot 示例项目,感受下 Knife4j 提供的 Swagger UI 界面。重启后,使用浏览器访问 http://127.0.0.1:8077/doc.html 地址,进行访问。如下图所示:

      Knife4j Swagger UI 界面

      是不是相比 swagger-dubbo 提供的 Swagger UI 界面更加优秀

      我们再来看看接口文档接口调试的界面,如下图所示:

      • 接口文档
      • 接口调试

      重要提示:此时,我们可以将 resources 目录下的 Swagger UI 的静态资源删除。

      艿艿这里不删除的原因,还是方便大家体验对比 = =

      3.4 再看 SwaggerConfiguration

      为什么我们要在 SwaggerConfiguration 配置类中,自定义一个 SwaggerResourcesProvider Bean 呢?原因是,它和 Swagger UI 界面的运行机制有关。

      ① Swagger UI 界面采用前后端分离的架构,通过请求 Swagger 定义的接口元数据 HTTP API,获得到每个接口的信息,展示成接口文档。

      可能有点难懂,我们来看看 dubbo-swagger 项目在 dubbo-provider-springboot 示例项目中,自定义实现的接口元数据 HTTP API的返回结果,如下图所示:

      接口元数据 HTTP API
      • paths 数组中,每一个元素对应一个接口的信息。而这些接口的信息,是通过扫描 Swagger 的注解所获取到。

      课外作业:对 dubbo-swagger 项目实现的接口元数据 HTTP API,可以后续看看 SwaggerDubboController 类的代码,比较简单。

      ② 在理解完 Swagger 定义的接口元数据 HTTP API 之后,再来看看 Swagger 定义的资源 HTTP API。Swagger Resource 资源的作用,用于将我们使用 SpringMVC 实现的 HTTP API 进行分组。并且,每个资源对应一个接口元数据 HTTP API,用于获取该分组的接口元数据。

      可能有点懵逼,我们来看看 dubbo-provider-springboot 示例项目中,资源 HTTP API的返回结果,如下图所示:

      资源 HTTP API
      • 蓝色部分】Swagger 默认实现了一套 Swagger 资源的逻辑,通过 InMemorySwaggerResourcesProvider 的 #get() 方法,进行获取。我们在使用 SpringMVC 实现的 HTTP API 接口,就属于该 Swagger 资源。

      • 红色部分swagger-dubbo 自定义了一套 Swagger 资源的逻辑,所以我们需要手动添加一个名字为 dubbo 的 Swagger Resource 分组,集成到 Swagger 体系中。

      现在,胖友是不是能够理解 SwaggerConfiguration 配置类的作用了。

      ③ 我们再使用浏览器访问 http://127.0.0.1:8077/doc.html 地址,进一步感受与理解。如下图所示:

      重看 Knife4j Swagger UI 界面

      可能胖友会有一个疑惑,为什么我们在使用 swagger-dubbo 提供的 Swagger UI 界面时,不用创建 SwaggerConfiguration 配置类呢?因为它不考虑存在多 Swagger Resource 资源的情况,直接请求 swagger-dubbo 提供的接口元数据 HTTP API。如下图所示:

      重看 swagger-dubbo Swagger UI 界面 Swagger UI 界面

      3.5 其它解决方案

      除了接入 Knife4j 作为 Swagger UI 界面的方案外,还有两种方案:

      方案一,将 swagger-dubbo 提供的 Swagger UI 的静态资源,部署到 Nginx 下。这样,我们就可以访问 Nginx 下的 Swagger UI 界面,填写需要查看 Dubbo 服务的接口元数据 HTTP API 即可。如下图所示:

      swagger-dubbo` Swagger UI 界面 Swagger UI 界面

      方案二,将 swagger-dubbo 提供的 Swagger UI 的静态资源,打包成名字为 swagger-dubbo-uijar 包。这样,每个 Dubbo 服务提供者的项目,引入该 jar 包即可。具体可参考 Knife4j 的 knife4j-spring-ui 的做法,如下图所示:

      knife4j-spring-ui

      4. 接入 YApi 统一管理

      随着 Dubbo 服务越来越多,我们需要一个 API 平台能够查看到所有的 Dubbo 服务的 RPC API 接口信息。目前,艿艿比较推荐和使用的 YApi 平台,主要原因是 YApi 可以采集 Swagger 提供的接口元数据 HTTP API,自动进行同步接口信息的同步。

      秃头提示:对 YApi 不太了解的胖友,可以去阅读下艿艿写的 《芋道 Spring Boot API 接口文档 Swagger 入门》 的「4. 更强大的 YApi」小节。

      具体的效果,胖友可以看看如下图:

      • 项目列表
      • 项目详情

      dubbo-swagger 提供的接口元数据 HTTP API,路径是 /swagger-dubbo/api-docs。至于如何配置定时采集,可参考如下图:

      定时采集

      666. 彩蛋

      至此,我们通过多个开源项目的组合,实现 Dubbo 的接口文档接口调试的功能。简单来总结下:

      • 基于 Swagger Dubbo 项目,实现 Dubbo RPC API 的接口文档接口调试的功能。

      • 基于 Knife4j 项目,提供更强大的 Dubbo Swagger UI 界面。

      • 基于 YApi 项目,作为统一的 API 管理平台,可以查看所有 Dubbo 项目的 API 接口文档、进行接口调试

      当然,如果我们要将该方案落地到公司的 Dubbo 项目中,还有一些事情需要去做:

      • 1、将修改后的 dubbo-swagger 项目,重新编译打包,推送到公司的 Nexus 私服。毕竟,咱也不知道 dubbo-swagger 项目啥时候会支持 Dubbo 2.7.0 开始的版本。

      • 2、编写公司的 Swagger Dubbo Starter,将对 Knife4j 的整合进行自动配置。

      • 3、尝试基于 YApi 提供的 mock 功能,实现 Dubbo 服务的 mock 能力,以便更好的并行开发。


      End~继续抠脚。

      我是艿艿,一个每天徘徊在煞笔牛啤的死胖子。



      欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

      已在知识星球更新源码解析如下:

      最近更新《芋道 SpringBoot 2.X 入门》系列,已经 20 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

      提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

      获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

      兄弟,一口,点个!????

    展开全文
  • Dubbo动态调用实现

    千次阅读 2018-09-18 19:50:31
    Dubbo常用使用方式大家都比较熟悉,确定好服务的与服务之间调用关系,确定好相关接口参数,基于spring配置好,启动provider,然后启动consumer去调用相关服务。但有这样的一种场景: 所有的Provider的接口都相同,但...

    问题提出

    Dubbo常用使用方式大家都比较熟悉,确定好服务的与服务之间调用关系,确定好相关接口参数,基于spring配置好,启动provider,然后启动consumer去调用相关服务。但有这样的一种场景:

    • 所有的Provider的接口都相同,但每个系统有自己的不同实现。例如系统A和B都提供com.HelloService服务,但具体实现不一样,需要Consumer端根据传入参数来区分开来并调用
    • Dubbo的Consumer端需要在运行时才知道调用具体的Dubbo服务,而这个<dubbo:reference/>并没有在spring的bean中配置

    解决方案

    同一个服务不同实现版本

    先来看第一种场景,可以通过配置group 的方式实现同一个服务的不同实现版本:

    提供者dubbo端配置:
    <dubbo:service interface="com.HelloService"  group="groupA" ref="helloService" />
    消费者consumer配置:
    <dubbo:reference id="helloService"interface="com.HelloService" group="groupA"/>
    说明:只有相同group的才能进行匹配,若要实现消费者任意调用提供者的某个服务,只需要把group设置为“*”,即:
    
    <dubbo:reference interface="com.HelloService" group="*" id="helloService"/>
    

    动态调用

    需要在实际使用时,构造出Consumer端的服务类,并通过上述的group的方式区分不同的服务实现,如下:

    public HelloService getInvokeService(String group) {
            ApplicationConfig application = new ApplicationConfig();
            application.setName("dubboConsumer");
            
            RegistryConfig registry = new RegistryConfig();
            registry.setAddress("127.0.0.1:2181");
            registry.setProtocol("zookeeper");
    
            ReferenceConfig<HelloService> referenceConfig = new ReferenceConfig<>();
            referenceConfig.setApplication(application);
            referenceConfig.setRegistry(registry);
            referenceConfig.setGroup(group);
            referenceConfig.setInterface(HelloService.class);
           return referenceConfig.get();
        }
    

    性能优化

    上述实现已经可以满足我们提出的两个要求,但是存在性能问题,因为每次调用该方法,都需要重新生成一个新的ReferenceConfig,并且调用get()方法获取一个代理的实现,该实现封装了与注册中心的连接以及与提供者的连接。为了能够重用该连接,可以将其缓存,这里使用dubbo内置的简单缓存工具类进行缓存,实现代码如下:

     public HelloService getInvokeService(String group) {
            ApplicationConfig application = new ApplicationConfig();
            application.setName("dubboConsumer");
            
            RegistryConfig registry = new RegistryConfig();
            registry.setAddress("127.0.0.1:2181");
            registry.setProtocol("zookeeper");
    
            ReferenceConfig<HelloService> referenceConfig = new ReferenceConfig<>();
            referenceConfig.setApplication(application);
            referenceConfig.setRegistry(registry);
            referenceConfig.setGroup(group);
            referenceConfig.setInterface(HelloService.class);
             ReferenceConfigCache cache = ReferenceConfigCache.getCache();
           return cache.get(referenceConfig);
        }
    

    参考文献:Dubbo高级特性实践-泛化调用

    展开全文
  • 服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消费方消费服务,所以基于这一点可以抽象出服务提供方(Provider)和服务消费方(Consumer)两个角色。关于注册中心、协议...
  • dubbo 返回给你的HelloService 就是 一个简单的HelloService 子类吗? 骚年,跟着哥撸一撸代码看看究竟再下定言。

    上一篇文章仔细的分析了 ,由 refer 方法 获取的 代理对象的过程,以及最后获取到的代理对象的结构,但是结尾比较草,本节仔细分析 字节码生成的代理对象。

    结构

    上一篇文章结尾草草的给出了 代理对象的结构图,但是并没有对(T) PROXY_FACTORY.getProxy(invoker)进行 整体上的分析:
    在这里插入图片描述
    首先在 Dubbo 的 ProxyFactory中,有两个字节码工具类,一个是JdkProxyFatory,另一个是 JavassistProxyFactory
    具体SPI 文件如下:

    stub=org.apache.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
    jdk=org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory
    javassist=org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory
    

    并且默认使用的是 JavassistProxyFactory 作为字节码工具。
    StubProxyFactoryWrapper 则主要是要执行优先于 ProxyFactory 的一些逻辑,这里主要是在 getProxy 对 配置了 stublocal 属性 进行一些额外处理。

    在 JavassistProxyFactory中的构造方法如下:

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

    上面构造方法主要是使用字节码工具,将多个 interfaces对象(包括了 EchoService)生成一个代理对象,再使用 InvokerInvocationHandler 包装起来。

    ProxyFactory$Adaptive 源码

    通过一定方式,获取到了 ProxyFactory$Adaptive 源码,便于我们后面分析:

    package org.apache.dubbo.rpc;
    import org.apache.dubbo.common.extension.ExtensionLoader;
    public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
        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");
            org.apache.dubbo.common.URL url = arg2;
            String extName = url.getParameter("proxy", "javassist");
            if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
            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);
    }
        public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) 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.getParameter("proxy", "javassist");
            if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
            org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
            return extension.getProxy(arg0, arg1);
    }
        public java.lang.Object getProxy(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.getParameter("proxy", "javassist");
            if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
            org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
            return extension.getProxy(arg0);
    }
    }
    

    PROXY_FACTORY.getProxy(invoker) 详解

    AbstractProxyFactory 代码示例:

    
        @Override
        public <T> T getProxy(Invoker<T> invoker) throws RpcException {
        // 不是泛化调用
            return getProxy(invoker, false);
        }
    
        @Override
        public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {
            Class<?>[] interfaces = null;
            String config = invoker.getUrl().getParameter(INTERFACES);
            if (config != null && config.length() > 0) {
                String[] types = COMMA_SPLIT_PATTERN.split(config);
                if (types != null && types.length > 0) {
                    interfaces = new Class<?>[types.length + 2];
                    interfaces[0] = invoker.getInterface();
                    interfaces[1] = EchoService.class;
                    for (int i = 0; i < types.length; i++) {
                        // TODO can we load successfully for a different classloader?.
                        interfaces[i + 2] = ReflectUtils.forName(types[i]);
                    }
                }
            }
            if (interfaces == null) {
            // 加入EchoService.class
                interfaces = new Class<?>[]{invoker.getInterface(), EchoService.class};
            }
    
            if (!GenericService.class.isAssignableFrom(invoker.getInterface()) && generic) {
            // 加入GenericService.class
                int len = interfaces.length;
                Class<?>[] temp = interfaces;
                interfaces = new Class<?>[len + 1];
                System.arraycopy(temp, 0, interfaces, 0, len);
                interfaces[len] = com.alibaba.dubbo.rpc.service.GenericService.class;
            }
    
            return getProxy(invoker, interfaces);
        }
    

    以下将一步一步,钻进getProxy 看其具体实现:

    1. ProxyFactory$Adaptive,会先检查参数。
    2. 通过proxy 参数获取使用的代理工厂,没有则使用默认的 javassist,使用 ExtensionLoader 的 SPI 方式获取。
    3. Adaptive 类中,获取的 extension 为 使用 StubProxyFactoryWrapper 包装后的 ProxyFactory,所以会先执行StubProxyFactoryWrappergetProxy 方法,而是否会执行 具体 ProxyFactorygetProxy 方法,则需要看 Wrapper 类的逻辑。
      在这里插入图片描述
    4. 进入 StubProxyFactoryWrappergetProxy,最终会先调用 AbstractProxyFactory,这里面的getProxy 方法主要是判断是否需要往代理生成类中再加入 GenericService 用于泛化调用。
    5. 而后调用子类的getProxy,这里使用 JavassistProxyFactory 为例:
        @Override
        @SuppressWarnings("unchecked")
        public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
            return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
        }
    
    1. 使用字节码工具,将传入的interfaces类,以及 参数 InvokerInvocationHandler,动态生成一个新的代理类并返回。
      这里为什么是多个呢?在使用Dubbo 的EchoService 及 泛化调用时,都是使用强转的形式,所以这里就传入多个interfaces,才保证了强转不会出错。
    2. 在使用 javassist 构造 Proxy.getProxy 时候,截取了字节码class 反编译后的 java 代码,可以便于理解
      在具体的 JavassistProxyFactorypublic <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) 依次会生成两个类:一个是Proxy0的包装类,另一个是 proxy0,即具体功能存放类。

    proxy0 代理类

    public class proxy0 implements DC, EchoService, HelloService {
        public static Method[] methods;
        private InvocationHandler handler;
    
        public String hello(String var1) {
            Object[] var2 = new Object[]{var1};
            Object var3 = this.handler.invoke(this, methods[0], var2);
            return (String)var3;
        }
    
        public Object $echo(Object var1) {
            Object[] var2 = new Object[]{var1};
            Object var3 = this.handler.invoke(this, methods[1], var2);
            return (Object)var3;
        }
    
        public proxy0() {
        }
    
        public proxy0(InvocationHandler var1) {
            this.handler = var1;
        }
    }
    

    Proxy0 代理类

    public class Proxy0 extends Proxy implements DC {
        public Object newInstance(InvocationHandler var1) {
            return new proxy0(var1);
        }
    
        public Proxy0() {
        }
    }
    
    

    分析在 JavassistProxyFactory 中的 以下方法:

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

    从上面代码中可以有以下收获:

    1. Proxy.getProxy(interfaces) 返回的是一个 Proxy0 对象,注意是大写的P
    2. 最后调用 Proxy0newInstance 方法,实例化一个包装类型的 proxy0,注意是小写的
    3. dubbo 里面的 EchoService 或者 泛化调用,都是通过javassist 字节码工具,以实现方法形式实现,所以才可以在业务层面强转成功。
    4. 对于返回的代理类,也实现对应 refer 的接口,例如上述实现的 HelloService
    5. DC 是dynamic code 缩写,只是一个标识,说明是动态生成的类。
    6. 对于手动执行方法,都是调用 InvokerInvocationHandlerinvoke 方法

    而对于 InvokerInvocationHandler 中invoker 及后面调用逻辑,且看下一篇文章分析。

    觉得博主写的有用,不妨关注博主公众号: 六点A君。
    哈哈哈,Dubbo小吃街不迷路:
    在这里插入图片描述

    展开全文
  • ❤ Java极客技术,你值得星标的纯原创公众号上篇文章如果有人问你 Dubbo 中注册中心工作原理,就把这篇文章给他[1]大致了解了注册中心作用以及 Dubbo Regi...
  • 加载到JVM生成一个实例对象的开发流程。 但是有没有想过其实没有Java文件,也没有编译好的class文件,我们照样可以向JVM中添加一个类实例呢? —》 javassist就可以完成这种骚操作。 而且读过dubbo源码的人肯定都...
  • Dubbo 服务引入

    千次阅读 2020-01-12 22:28:07
    本篇文章侧重于 Dubbo 服务引入的实现细节,主要包括:服务引入入口,获取服务地址列表,启动 qos server,注册消费者,构建路由策略链,订阅数据,notify,服务引入,发布服务引入事件,构建 filter 链,合并 ...
  • SpringBoot+Dubbo+Mybatis代码生成器,基于mysql一键生成rest、rpc、service、domain、dao模块,并集成了swagger。启动类在main模块下。 数据表中要有id、version、ts、yn几个基础字段,详见demo文件夹下的示例。...
  • 面试杀手锏之Dubbo服务调用过程

    万次阅读 多人点赞 2020-09-14 08:51:58
    前言 前面我已经带着大家过了服务暴露和服务引入两个流程了,而这两个流程就是为了服务的调用,今天丙就带着大家来过一遍 Dubbo 服务调用流程。 看完今天的服务调用流程,基本上Dubbo的核心过程就完整的串联起来了...
  • Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和Spring 框架无缝集成。 Dubbo主要核心部件: Remoting:网络通信框架,实现了sync-over-async和...
  • 上篇文章《【dubbo源码解析】— dubbo服务暴露+服务消费(RPC调用)底层原理深入探析》非常详细的介绍了dubbo的RPC远程调用过程,这里做一个简单的回顾。 【首先】 需要明确的是,在dubbo的RPC远程调用过程中,最...
  • Dubbo 服务导出

    2020-01-07 22:57:39
    本篇文章侧重于 Dubbo 服务导出的实现细节,主要包括:服务导出入口,加载注册中心地址,创建 invoker 对象,启动 qos server,构建 filter 链,服务导出,发布服务导出事件,注册服务地址,订阅等。
  • Dubbo

    千次阅读 多人点赞 2019-08-07 20:15:59
    Dubbo什么是Dubbo为什么要用Dubbo什么是分布式?为什么要分布式?...简单来说Dubbo就是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。 Dubbo 目前已经有...
  • 本文对应源码地址:https://github.com/nieandsun/dubbo-study 文章目录1 Protocol+Invoker简介 ... RMI — 可以参看我的上篇文章《【dubbo源码解析~番外篇】— JDK和Spring的RMI使用姿势简介》 但是.
  • 现在有一个模块是dubbo的provider,想生成provider的接口文档,不知道swagger能不能实现,求各位大佬指点
  • DUBBO服务的引用过程

    千次阅读 2019-08-10 16:12:22
    前边两篇文章简单的分析了DUBBO服务的暴露以及注册的过程。既然DUBBO服务已经注册...DUBBO引用服务的XML配置(DUBBO源码示例) <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="htt...
  • Dubbo系列之服务注册与发现

    万次阅读 2019-07-21 20:02:02
    文章目录一、分布式基本理论1.1、分布式基本定义1.2 架构发展演变1.3、RPC简介二、Dubbo理论简介三、Dubbo环境搭建3.1 Zookeeper搭建3.2 Dubbo管理页面搭建四、Dubbo服务注册发现例子4.1、业务场景4.2、api工程创建...
  • 点击上方Java后端,选择设为星标优质文章,及时送达Introducesmart-doc是一款同时支持JAVA REST API和Apache Dubbo RPC接口文档生成的工具,...
  • dubbo服务链路跟踪

    千次阅读 2019-09-06 11:44:18
    目前很多业务使用微服务架构,服务模块划分有这2种方式: 服务功能划分 业务划分 不管哪种方式,一次接口调用都需要多个服务协同完成,其中一个服务出现问题,都会导致最终失败,虽然有logback + kafka + ELK 这样...
  • dubbo rpc接口文档生成

    千次阅读 2020-07-05 23:06:22
    在国内dubbo成为很多互联网公司高并发...当然本文的重点不是介绍dubbo的使用,而是介绍如何利用smart-doc工具来生成dubbo的rpc内部接口文档。smart-doc因为其基于注释和java接口定义自动推导的理念,开源以来受到国内
  • 疑惑一:为什么在Spring中我们能像注入普通本地服务JavaBean一样注入远程的Dubbo服务Bean? 我们知道,Dubbo服务调用封装成普通的Spring的Bean,于是我们可以像使用本地的Spring Bean一样,来调用远端的Dubbo服务...
  • 一般选择DUBBO来进行服务管理,都是在分布式应用的前提下,涉及到多个子系统之间的调用,DUBBO所做的事情就是维护各个子系统暴露的接口和自动发现对应接口的远程地址从而实现分布式RPC服务管理。 有了上面前提之后...
  • dubbo服务暴露

    千次阅读 2019-01-20 20:35:42
    ServiceBean#onApplicationEvent(ContextRefreshedEvent) ...首先校验该service的配置是否为空,则加载dubbo:provider、dubbo:module、dubbo:application缺省配置,若还为空则加载dubbo.properties的配置。...
  • 本文通过Dubbo以定义一个interface,各个通道方来实现这个接口。通过group来区分不同的通道 有需要的同学可以下载示例代码 项目结构如下:interface,provider,consumer 1.创建接口 public interface ...
  • Dubbo服务注册和发现

    千次阅读 2019-02-11 17:51:47
    Dubbo服务发现:先到注册中心寻找服务的List,选中一个,然后和被调用方建立NIO连接,传输数据包,进行方法调用  利用心跳去检测注册的服务是否存活;  在Spring启动后会利用DubboNamespaceHandler解析配置文件中的...
  • dubbo实现动态调用

    2017-03-13 11:37:00
    在支付网关的业务中,在用户支付成功/失败后需要异步通知业务方,这个时候业务方是一个变数,支付网关可以定义一个interface,各个业务方来实现这个接口。...dubbo://192.168.18.234:20883/com.wy.payment.servic...
  • 不管在服务暴露还是服务消费场景下,Dubbo框架都会根据优先级对配置信息做聚合处理,目前默认覆盖策略主要遵循以下几点规则: -D 传递给JVM参数优先级最高,比如-Ddubbo.protocol.port=20880。 代码或XML配置优先级...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 39,697
精华内容 15,878
关键字:

动态生成dubbo服务