美文网首页
Dubbo SPI (service provider inte

Dubbo SPI (service provider inte

作者: Double_winter | 来源:发表于2019-02-08 19:03 被阅读0次

    最近拜读dubbo 的源码,其构架设计是很精彩,基于插件式的构架方式,灵活性可见一般。此外dubbo 框架难得基 服务降级,服务路由,服务发现与注册,服务loadbalance 和 支持多种协议于一身的优秀开源框架,很是值得学习和研究细细读来的。此外整个框架的class 加载是根据统一的URL参数。各个层次结构分明,清晰,可扩展性特别好。读次源码感叹一下!

    曾经我再封装一些框架的时候,总是加入一些不必要的依赖,比如封装ribbon,支持多个配置中心的时候,就会发现,会把多个配置中心的依赖都加入进去。开发支持多个校验引擎的时候,心理也是在琢磨着如何更好的切换到不同的校验引擎。Dubbo SPI 提供一种很好的思路。根据统一的URL 配置信息,通过代理类按需加载!

    前提:

    (1). 理解 jdk SPI 

    (2). 理解AVAssist 的产生class 字节码,Dubbo 中默认使用AVAssist,主要用来生成代理类

    (3). 反射和动态代理(dubbo 的自适应扩张,就是依靠动态代理)

    好了,我们来看看dubbo SPI具体的特性。

    (1): dubbo SPI 根据 protocol=com.XX.XXX.dubboProtocol, 的方式加载

    (2): dubbo SPI 的IOC 特性,dubboProtocol类的setXXX 方法,会通过反射的方式,将相应的class instance, inject 到 dubboProtocol, 通过setXXX的方法。 后面将会有源码。

    (3):dubbo SPI 的AOP 特性, ProtocolFilterWrapper 和 ProtocolListenerWrapper这两个类含有protocol 单构造器,放置在loader的私有属性cachedWrapperClasses。ProtocolFilterWrapper在服务的暴露与引用的过程中,根据key 是provider还是consumer来构建服务提供者和消费者调用过滤链。因此具有AOP 的性质。ProtocolListenerWrapper也是在服务暴露与引用的过程中调用listener链。

    首先我们需要理解ExtensionLoader, ExtensionFactory这个类。ExtensionFactory 的实现类有 SpiExtensionFactory, SpringExtensionFactory. 默认会加载AdaptiveExtensionFactory (@Adaptive 的注解)实现类。

    (1): SpiExtensionFactory:加载有@SPI 的注解的接口实现类。

    (2):SpringExtensionFactory: 加载spring context bean 的beans。

    默认的实现类AdaptiveExtensionFactory,依赖 List<ExtensionFactory> factories. 当调用 ExtensionFactory 时候,会循环SpiExtensionFactory和SpringExtensionFactory,获得 Class type 的 extension。

    @SPI

    public interface ExtensionFactory {

        T getExtension(Class type, String name);

    }

    接下来就是进入到 ExtensionLoader,SpiExtensionFactory 的 getExtension 服务,会调用ExtensionLoader的 getExtensionLoader方法。然后通过调用 getAdaptiveExtension,加载自适应点。好了,我们进入ExtensionLoader, getExtensionLoader 去看看会发生什么。

    但是再这之前,我们看看加载类的配置。配置文件的路径如下:

    private static final StringSERVICES_DIRECTORY ="META-INF/services/";

    private static final StringDUBBO_DIRECTORY ="META-INF/dubbo/";

    private static final StringDUBBO_INTERNAL_DIRECTORY =DUBBO_DIRECTORY +"internal/";

    ExtensionLoader    会加载所有classpath 下,该目录的实现类。其implement 的接口都必须有@SPI的注解。

    里面的每个类的结构都是 “name” = “com.XXX.XXXX.registryProtocol”的方式来实现的。首先我们来看下ExtensionLoader主要逻辑:

    @SuppressWarnings("unchecked")

    public static ExtensionLoadergetExtensionLoader(Class 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 loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);

        if (loader ==null) {

    EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));

            loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);

        }

    return loader;

    }

    先从EXTENSION_LOADERS cache 中取出 type 类型的 ExtensionLoader, 比如protocol。 如果没有就会new 一个ExtensionLoader. 我们来看一下new ExtensionLoader的逻辑。 

    private ExtensionLoader(Class type) {

    this.type = type;   

     objectFactory = (type == ExtensionFactory.class ?null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

    }

    如果不是ExtensionFactory类型的,就获取一个自适应扩张类。getAdaptiveExtension()。

    那么getAdaptiveExtension()是什么逻辑呢?

    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;

    }

    首先也是从cachedAdaptiveInstance 获取, 如果获取不到,通过调用createAdaptiveExtension()。 我们看看createAdaptiveExtension的逻辑。

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

        }

    }

    首先是通过getAdaptiveExtensionClass,然后实列化,通过injectExtension 来注入,injectExtension就是上面提到的 IOC 特性,会通过setXXX() 方法,注入对象实列。我们先看

    private Map>getExtensionClasses() {

    Map> classes =cachedClasses.get();

        if (classes ==null) {

    synchronized (cachedClasses) {

    classes =cachedClasses.get();

                if (classes ==null) {

    classes = loadExtensionClasses();

                    cachedClasses.set(classes);

                }

    }

    }

    return classes;

    }

    首先从cachedClasses 缓存中获取class,如果获取不到,就调用loadExtensionClasses();,加载extension classes. 

    private Map>loadExtensionClasses() {

    final SPI defaultAnnotation =type.getAnnotation(SPI.class);

        if (defaultAnnotation !=null) {

    String value = defaultAnnotation.value();

            if ((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> extensionClasses =new HashMap>();

        loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);

        loadDirectory(extensionClasses, DUBBO_DIRECTORY);

        loadDirectory(extensionClasses, SERVICES_DIRECTORY);

        return extensionClasses;

    }

    首先是设置cachedDefaultName 的默认名字, 然后去load 各个目录下的class, key = value 的形式。

    Map> extensionClasses =new HashMap>();

    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);

    loadDirectory(extensionClasses, DUBBO_DIRECTORY);

    loadDirectory(extensionClasses, SERVICES_DIRECTORY);

    然后进入loadResource 的方法,我们就明白key value 的形式是如何解析的。最后我们调用loadClass 方法,将加载好的class 放入到对应的缓存中。带有adaptive 注解的放入cachedAdaptiveClass, wrapper的class,放入到 cachedWrapperClasses中去。标记有@Active 的类注入到cachedActivates 缓存中去。

    将所有的key  = value 形式加载完毕之后,我们要看看IOC 是如何作用的。

    privateTinjectExtension(T instance){

        try {

            if (objectFactory != null) {

                // 遍历目标类的所有方法            for (Method method : instance.getClass().getMethods()) {

                    // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public                if (method.getName().startsWith("set")

                        && method.getParameterTypes().length == 1                    && Modifier.isPublic(method.getModifiers())) {

                        // 获取 setter 方法参数类型                    Class<?> pt = method.getParameterTypes()[0];

                        try {

                            // 获取属性名,比如 setName 方法对应属性名 name                        String property = method.getName().length() > 3 ?

                                method.getName().substring(3, 4).toLowerCase() +

                                method.getName().substring(4) : "";

                            // 从 ObjectFactory 中获取依赖对象                        Object object = objectFactory.getExtension(pt, property);

                            if (object != null) {

                                // 通过反射调用 setter 方法设置依赖                            method.invoke(instance, object);

                            }

                        } catch (Exception e) {

                            logger.error("fail to inject via method...");

                        }

                    }

                }

            }

        } catch (Exception e) {

            logger.error(e.getMessage(), e);

        }

        return instance;

    }

    通过 Object object = objectFactory.getExtension(pt, property) 和 method.invoke(instance, object) 将adaptive 的类注入的 带有setXXX方法类中。其中objectFactory就是spiExtensionFactory。 

    那么有没有想过到底是如何使用 自适应扩展点的? 你看,上面通过IOC 和 spiExtensionFactory 将所有的key = value 形式的类都加载好了,存储的形式是map <key, class<?> class>. 那我们什么时候用哪个接口的实现类呢? 比如说:

    package com.alibaba.dubbo.rpc; 下的 protocol。 实现类有

    那应该使用哪个实现类呢? 这就是自适应扩展点。首先会产生自适应扩展点的代理,然后 在通过URL 的配置信息,传入name, 获取相应的加载类,这就是key value 的原因。 

    代理类是什么样呢?代理类是通过javassit 产生的, 请看下面:

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

    }}

    String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() ); 就是从 URL 获取 name, 如果获取不到,就默认使用dubbo,通过 getExtension(extName),就可以获取到 加载类: DubboProtocol

    那URL 是啥样子呢:dubbo://172.27.238.135:20880/com.mastercard.api.service.DemoService?anyhost=true&application=dubbo-provider&bind.ip=172.27.238.135&bind.port=20880&default.timeout=5000&dubbo=2.6.2&generic=false&interface=com.mastercard.api.service.DemoService&methods=sayHello&pid=71712&revision=1.0.0&side=provider&timestamp=1549356336726&version=1.0.0

    其实里面的很多内容很精彩,需要自己慢慢品味。后面具体分析下自适应扩展点。

    相关文章

      网友评论

          本文标题:Dubbo SPI (service provider inte

          本文链接:https://www.haomeiwen.com/subject/bvnnsqtx.html