zoukankan      html  css  js  c++  java
  • 一、dubbo源码从入门到放弃-SPI

    一、dubbo源码从入门到放弃-SPI

    @update:2016-06-28 00:44:46
    @author:张三金 去哪儿网高级工程师
    未完待续...

    1.引

    如果要讲dubbo源码,那么要从SPI开始(SPI自行google),因为dubbo所有的功能都是通过自己实现的一套SPI机制来扩展的,可以理解为dubbo的所有功能都拆分并且抽象为interface,通过SPI查找这些interface的实现,并且通过url组合起来,就成了一个完整的rpc框架.

    2.ExtensionLoader

    ExtensionLoader是dubbo内部的SPI实现,dubbo的所有组件都通过ExtensionLoader获取,本文以最常见的interface Filter为例,dubbo里有很多的filter,并且支持码农自定义filter,dubbo的filter全是通过SPI查找的,所以非常易于扩展.

    com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper#buildInvokerChain方法中拿到当前service所有的filter的实现,然后执行.

    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    

    2.1getExtensionLoader

    getExtensionLoader方法会为每一个.class new 一个ExtensionLoader实例,并缓存起来

        private ExtensionLoader(Class<?> type) {
            this.type = type;
            objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
        }
    

    2.2getAdaptiveExtension

    在dubbo中,需要扩展的功能需要在其interface上打到@SPI注解
    在dubbo SPI中,interface的一个实现叫做Extension,Extension实现了这个接口的功能,一个interface有许多的Extension

    AdaptiveExtension叫做适配Extension,一个AdaptiveExtension的作用是从所有的Extension中,根据当前的url配置策略,选出指定的的Extension

    上面的代码通过getAdaptiveExtension()方法拿到当前interface的AdaptiveExtension

    首先会拿到所有的接口实现
    code:ExtensionLoader#loadExtensionClasses

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

    loadExtensionClasses

    1. 从interface上拿到注解@SPI,取出默认实现的名字
    2. 调用loadFile加载interface的实现类

    loadFile会到3个目录下把所有的接口实现类加载进来

    1. META-INF/dubbo/internal/
      dubbo内部实现都放在这个目录下
    2. META-INF/dubbo/
    3. META-INF/services/

    例如下面为dubbo内部实现filter的SPI文件
    file:META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter

    echo=com.alibaba.dubbo.rpc.filter.EchoFilter
    generic=com.alibaba.dubbo.rpc.filter.GenericFilter
    genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
    token=com.alibaba.dubbo.rpc.filter.TokenFilter
    accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
    activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
    classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
    context=com.alibaba.dubbo.rpc.filter.ContextFilter
    consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
    exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
    executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
    deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
    compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
    timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
    monitor=com.alibaba.dubbo.monitor.support.MonitorFilter
    validation=com.alibaba.dubbo.validation.filter.ValidationFilter
    cache=com.alibaba.dubbo.cache.filter.CacheFilter
    trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
    future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter
    
    

    2.3loadFile

    SPI文件给出了所有实现的完全限定类名,那么可以通过反射机制加载对应的Class

    Class<?> clazz = Class.forName(line, true, classLoader);
    

    加载的Class会被归为3类

    1. Extension,接口的实现,实现接口功能的类
      Holder<Map<String, Class<?>>> cachedClasses持有其引用
    2. AdaptiveExtension,适配指定Extension,打上了@Adaptive注解的类会被加载为AdaptiveExtension,这个类只能有一个
      Class<?> cachedAdaptiveClass持有其引用
    3. wrapper,用装饰者模式包装Extension的类,wrapper有且只有一个以interface为参数的构造函数
      Set<Class<?>> cachedWrapperClasses持有其引用

    加载AdaptiveExtension,通过判断@Adaptive注解

    if (clazz.isAnnotationPresent(Adaptive.class)) {
                if(cachedAdaptiveClass == null) {
                    cachedAdaptiveClass = clazz;
                } else if (! cachedAdaptiveClass.equals(clazz)) {
                    throw new IllegalStateException("More than 1 adaptive class found: "
                            + cachedAdaptiveClass.getClass().getName()
                            + ", " + clazz.getClass().getName());
                }
            }
    

    加载wrapper extension

    try {
                clazz.getConstructor(type);
                Set<Class<?>> wrappers = cachedWrapperClasses;
                if (wrappers == null) {
                    cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                    wrappers = cachedWrapperClasses;
                }
                wrappers.add(clazz);
            } catch (NoSuchMethodException e) {
                //不是wrapper extension
            }
    

    前面两个都不是,那就是Extension,直接放入Holder<Map<String, Class<?>>> cachedClasses

    3.注解

    3.1@SPI

    SPI注解标记在interface上,表示这个interface的实现通过SPI加载,value表示有多个实现时,默认使用指明的实现

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    public @interface SPI {
    
        /**
         * 缺省扩展点名。
         */
    	String value() default "";
    
    }
    

    reference

    interface Filter

    @SPI
    public interface Filter {
    	Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
    
    }
    
  • 相关阅读:
    Centos6.5系统压力测试过程大量TIME_WAIT
    几种常用的数据库连接池
    weblogic弱密码检测
    ubuntu系统查看已安装的软件
    Flask Web中用MySQL代替SQLite
    SQLALCHEMY_TRACK_MODIFICATIONS adds significant异常的解决方法
    安装ipython时python setup.py egg_info错误的解决办法
    python manage.py runserver指定端口和ip
    Python连接mysql出错,_mysql_exceptions.OperationalError: (1045, "Access denied for user 'root'@'localhost' (using password: YES)")
    linux重启服务的脚本命令
  • 原文地址:https://www.cnblogs.com/kindevil-zx/p/5603643.html
Copyright © 2011-2022 走看看