【Dubbo】Adaptive

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了【Dubbo】Adaptive相关的知识,希望对你有一定的参考价值。

参考技术A 这里指的Adaptive是dubbo中的一个注解: @Adaptive 。从这个注解的定义上我们可以看到 @Target(ElementType.TYPE, ElementType.METHOD) ,它表明 @Adaptive 可以用在类、接口和方法上。
@Adaptive 代表dubbo的SPI的动态适应能力,如果@Adaptive注解在扩展点实现类上那个该扩展点就是一个包装真实扩展点实例的装饰类;如果注解在方法上那么扩展点的实例就是一个动态代理类,例如Protocol$Adaptive对象。

adaptive设计的目的是为了识别固定已知类和扩展未知类。

代表人工实现,实现一个装饰类(设计模式中的装饰模式),它主要作用于固定已知类,目前整个系统只有2个,AdaptiveCompiler、AdaptiveExtensionFactory。

代表自动生成和编译一个动态的Adpative类, 含有@Adaptive的方法中都可以根据方法参数动态获取各自需要真实的扩展点。 它主要是用于SPI,因为spi的类是不固定、未知的扩展类,所以设计了动态 $Adaptive 类;
ExtensionLoader.getAdaptiveExtension 方法会返回动态编译生成的 $Adaptive

例如: Protocol的spi类有injvm、dubbo、registry、filter、listener等很多未知扩展类, ExtensionLoader.getAdaptiveExtension 会动态编译Protocol$Adaptive的类,再通过在动态累的方法中调用 ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi类); 来提取对象。

在dubbo中一般会首先通过 ExtensionLoader.getAdaptiveExtension 获取Adaptive扩展。这个方法会首先在扩展点接口的所有实现类中查找类上是否有含有 @Adaptive 注解,如果有这样的类直接返回该类的实例,如果没有则会查找扩展点接口的方法是否有 @Adaptive 注解并动态编译一个类实现该接口并扩展这些含有 @Adaptive 注解的方法。

Protocol 所有扩展实现类上都没有 @Adaptive 注解,且扩展接口含有两个 @Adaptive 注解的方法: exporter() refer() ,所以dubbo会生成一个动态类 Protocol$Adaptive ,且它实现 Protocol 接口来扩展这两个Adaptive方法。扩展点接口和最终动态生成Protocol$Adaptive类如下:

解释一下 export(com.alibaba.dubbo.rpc.Invoker arg0) 方法:

这样就实现了每一个方法都可以根据方法参数动态获取各自需要真实的扩展点。

Dubbo源码学习(四ExtensionLoader 扩展点加载机制,Protocol$Adaptive,ProxyFactory$Adaptive,Cluster$Adaptive)

坚持一下,把源码看完,勤奋一点,不要在懒惰了,你已经落下别人很多了

环境配置:

jdk  1.7.0.17
dubbo 2.5.x
myeclipse 2014
idea 2017

SPI注解的接口

在dubbo中,采用了类似于java的spi机制进行扩展类加载

在dubbo.jar文件夹下有个META-INF/dubbo/internal文件夹下,定义了很多了扩展点加载类,如下图

这些文件名基本是类的全路径名,所有扩展类交由ExtensionLoader来统计进行加载处理

ExtensionLoader的属性和方法

private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);

    private static final String SERVICES_DIRECTORY = "META-INF/services/";
    private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
    private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";

    private static final Pattern NAME_SEPARATOR = Pattern.compile("\\\\s*[,]+\\\\s*");

    private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

    private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();

    // ==============================

    private final Class<?> type;

    private final ExtensionFactory objectFactory;

    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();

    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();

    private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
    private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
    private volatile Class<?> cachedAdaptiveClass = null;
    private String cachedDefaultName;
    private volatile Throwable createAdaptiveInstanceError;

    private Set<Class<?>> cachedWrapperClasses;

    private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();

ExtensionLoader 对于外部调用来说,主要的方法是上图标注的3个方法,

我们以ServiceConfig为例,分析如下代码

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

ExtensionLoader.getExtensionLoader

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)) 
        throw new IllegalArgumentException("Extension type(" + type +
                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    

    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;

 ExtensionLoader.getAdaptveExtension

public T getAdaptiveExtension() 
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) 
        if (createAdaptiveInstanceError == null) 
            synchronized (cachedAdaptiveInstance) 
                instance = cachedAdaptiveInstance.get();
                if (instance == null) 
                    try 
                        instance = createAdaptiveExtension();
                        cachedAdaptiveInstance.set(instance);
                     catch (Throwable t) 
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                    
                
            
         else 
            throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        
    

    return (T) instance;

private T createAdaptiveExtension() 
    try 
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
     catch (Exception e) 
        throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    

private Class<?> getAdaptiveExtensionClass() 
    getExtensionClasses();
    if (cachedAdaptiveClass != null) 
        return cachedAdaptiveClass;
    
    return cachedAdaptiveClass = createAdaptiveExtensionClass();

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);
 

由此我们大体可以得到最终生成的Protocol类 是通过写死的字符串来编译生成相应的对象,也就基本上大体了解了ExtensionLoader.getAdaptiveExtension 获取的类是有字符串拼接构建的,下面提出几个比较重要的Adaptive类代码

Protocol$Adaptive

public class Protocol$Adaptive 
    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   Invoker refer(Class arg0, URL arg1) throws com.alibaba.dubbo.rpc.RpcException 

        if (arg1 == null) throw new IllegalArgumentException("url == null");

        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])");
        Protocol extension = (Protocol) ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);

        return extension.refer(arg0, arg1);

    

    public  Exporter export(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");

        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 rom url(" + 	url.toString() + ") use keys([protocol])");

        Protocol extension =(Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);

        return extension.export(arg0);

    


ProxyFactory$Adaptive

public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory
    public java.lang.Object getProxy(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.getParameter("proxy", "javassist");
        if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + 	url.toString() + ") use keys([proxy])");
        ProxyFactory extension = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(extName);
        return extension.getProxy(arg0);
    
    public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, URL arg2) throws RpcException
        if (arg2 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg2;
        String extName = url.getParameter("proxy", "javassist");
        if(extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
        ProxyFactory extension =(ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(extName);
        return extension.getInvoker(arg0, arg1, arg2);
    

Cluster$Adaptive

public class Cluster$Adaptive implements Cluster 
    public Invoker join(Directory arg0) throws RpcException 
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument 			getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("cluster", "failover");
        if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.cluster.Cluster) 			name from url(" + url.toString() + ") use keys([cluster])");
        Cluster extension =(Cluster) ExtensionLoader.getExtensionLoader(Cluster.class).getExtension(extName);
        return extension.join(arg0);
    

 

以上是关于【Dubbo】Adaptive的主要内容,如果未能解决你的问题,请参考以下文章

dubbo实战篇:dubbo超时设置

Dubbo接口测试Dubbo接口调用Jmeter调用Dubbo接口,Jmeter测试Dubbo接口

Dubbo接口测试Dubbo接口调用Jmeter调用Dubbo接口,Jmeter测试Dubbo接口

dubbo教程-09-dubbo分布式事务 ? 看看dubbo作者怎么说 !

Dubbo学习(Dubbo2.6)

dubbo是如何启动的