dubbo AdaptiveExtension

AdaptiveExtension

自适应Extension,作者其实在使用Extension方和Extension之间插入AdaptiveExtension用来自适应,也可以说是适配。

所以,我们发现,这里其实有三种,

1,一种是自动生成的Adaptive根据url参数来选择具体实现;

2,一种是自己实现一个Adaptive,写自己的逻辑选择具体实现,比如AdaptiveExtensionFactory,Compiler。这种都是在ExtensionLoader中需要使用,一般是不会这么做的。

3,还有一种没有Adaptive,实现一个adapter,比如TelnetHandler。这种因为不能根据url参数来决定使用哪一个实现。

从结构上看AdaptiveExtension是很重要的角色,我们配置了一些插件,但是具体使用哪一个如果用硬编码自然是不可能的,而这种自适应的方式得益于dubbo的状态数据信息都可以通过URL来获取,被叫做以URL为总线的模式。而自适应即是根据这些信息来决定调用哪一个插件的实现。

ExtensionLoader是插件机制的核心类:

private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
// 获得AdaptiveExtension入口
public T getAdaptiveExtension() {
        // cachedAdaptiveInstance缓存
        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 extenstion " + type + ", cause: " + e.getMessage(), e);
        }
    }

    private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        // 依然没有cachedAdaptiveClass 说明没有配置@Adaptive 那就自动生成一个
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

       private Class<?> createAdaptiveExtensionClass() {
         // 组装java代码
        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);
    }

    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.set(classes);
                }
            }
        }
        return classes;
    }

    private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if(defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if(value != null && (value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if(names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                if(names.length == 1) cachedDefaultName = names[0];
            }
        }

        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }
    

以下方法都会触发loadFile,这个方法就会读取文件,解析出各个插件的class。有@Adaptive注解的会放入赋值给cachedAdaptiveClass。

下面以Transporter接口为例进行说明自动生成的代码:

@SPI("netty")
public interface Transporter {
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;

    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

动态生成类:

public class Transporter$Adaptive implements Transporter {

    public Server bind(URL arg0, ChannelHandler arg1) throws RemotingException {
        URL url = arg0;
        String extName = url.getParameter("server",
                url.getParameter("transporter", "netty"));
        Transporter extension = (Transporter) ExtensionLoader
                .getExtensionLoader(Transporter.class).getExtension(extName);
        return extension.bind(arg0, arg1);
    }

}

Adaptive机制是一个很好的设计,很好的解决多方案实现的适配问题,如果你遇到类似的代码架构的时候,多想一下更加有扩展性的设计。

时间: 2025-01-11 02:08:13

dubbo AdaptiveExtension的相关文章

从ExtensionLoader看Dubbo插件化

欢迎加入 DUBBO交流群:259566260 之前很多人问我Dubbo插件化是怎么实现的,我都是简单回答SPI.了解SPI的人知道,它只是提供一种协议,并没有提供相关插件化实施的接口,不像OSGI那样有一成套实施插件化API.它只是规定在META-INF目录下提供接口的实现描述文件,框架本身定义接口.规范,第三方只需要将自己实现在META-INF下描述清楚,那么框架就会自动加载你的实现,至于怎么加载,JDK并没有提供相关API,而是框架设计者需要考虑和实现的,并且在META-INF下面对实现描

Dubbo中SPI扩展机制解析

dubbo的SPI机制类似与Java的SPI,Java的SPI会一次性的实例化所有扩展点的实现,有点显得浪费资源. dubbo的扩展机制可以方便的获取某一个想要的扩展实现,每个实现都有自己的name,可以通过name找到具体的实现. 每个扩展点都有一个@Adaptive实例,用来注入到依赖这个扩展点的某些类中,运行时通过url参数去动态判断最终选择哪个Extension实例用. dubbo的SPI扩展机制增加了对扩展点自动装配(类似IOC)和自动包装(类似AOP)的支持. 标注了@Activat

Dubbo源码阅读笔记3

### 扩展点加载(ExtensionLoader) 每一种类型的扩展点都有一个ExtensionLoader实例 变量说明 public class ExtensionLoader<T> { // dubbo服务扫描目录 private static final String SERVICES_DIRECTORY = "META-INF/services/"; // dubbo扩展点配置扫描目录(自定义扩展时使用此目录) private static final Stri

dubbo怎么做自动注入的?

通过spi扩展加载的时候,都是通过extensionloader来得到extension的,比如获得一个exchanger: public static Exchanger getExchanger(String type) { return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);} 那么就是先拿到自己的extensionloader,然后利用这个loder去拿到extent-name对应的具

【Dubbo 源码解析】02_Dubbo SPI

Dubbo SPI:(version:2.6.*) Dubbo 微内核 + 插件 模式,得益于 Dubbo SPI .其中 ExtentionLoader是 Dubbo SPI 最核心的类,它负责扩展点的加载和生命周期管理. ExtensionLoader ExtensionLoader 类似于 Java SPI 的 ServiceLoader,负责扩展的加载和生命周期维护,它是 Dubbo SPI 最核心的类. 使用最频繁的 API 有如下几个: public static <T> Exte

dubbo源码分析01:SPI机制

一.什么是SPI SPI全称为Service Provider Interface,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件.这样可以在运行时,动态为该接口替换实现类. JDK提供了默认的SPI实现,但是Dubbo并未使用JDK提供的SPI,而是自己封装了一套.我们先来通过Dubbo官网给的两个例子简单了解下JDK和Dubbo的SPI是如何使用的. 1.1.JDK SPI示例 首先定义一个接口以及它的两个实现类 1 public interfac

dubbo源码分析之基于SPI的强大扩展

https://blog.csdn.net/luoyang_java/article/details/86609045 Dubbo采用微内核+插件体系,使得设计优雅,扩展性强.那所谓的微内核+插件体系是如何实现的呢!大家是否熟悉spi(service providerinterface)机制,即我们定义了服务接口标准,让厂商去实现(如果不了解spi的请谷歌百度下), jdk通过ServiceLoader类实现spi机制的服务查找功能 1.为什么不使用JDK SPI在dubbo中它实现了一套自己的

探究Dubbo的拓展机制: 下

承接上篇, 本篇博文的主题就是认认真真捋一捋, 看一下 Dubbo是如何实现他的IOC / AOP / 以及Dubbo SPI这个拓展点的 总览: 本篇的话总体上分成两部分进行展开 第一点就是 Dubbo在启动过程中加载原生的配置文件中提供的被@SPI标记的实现类: 第二就是Dubbo加载程序员后续添加进去的被@SPI标注的接口和实现类, 进而探究 Dubbo的IOC / AOP / 以及Dubbo SPI这个拓展点机制 环境的初始化 入口程序 如下代码是追踪的起点: 我也是看了好多遍才勉强将这

Dubbo

Dubbo Duboo是什么 DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架,每天为2,000+个服务提供3,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点.可以看出在阿里内部广泛应用,类似的还有Spring Cloud. 准备工作 准备两虚机,我这里用了CentOS7.2,加上本机可组成多提供者和消费者(当然一个虚机和不用虚机也可以)我准备的两台IP为:192.168.124.129(用于