前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >dubbo(一)SPI机制与实现路径

dubbo(一)SPI机制与实现路径

作者头像
虞大大
发布2020-09-18 15:11:40
8320
发布2020-09-18 15:11:40
举报
文章被收录于专栏:码云大作战码云大作战

一、dubbo启动流程

了解dubbo内核之前,我们先看下dubbo的启动流程,参考了本地启动时日志打印的dubbo启动整理出来的流程图:

(1)启动provider时,会把所有暴露的接口注册到注册中心,并会订阅动态配置(configuration)。

(2)启动consumer时,会订阅服务端接口、动态配置、负载均衡集群(providers、configuration、routers)。

(3)provider:订阅的动态配置信息变更时,注册中心会推送动态配置信息(configuration)给provider。

consumer:订阅内容变更时,会推送订阅的信息(providers、configuration、routers)给consumer。

(4)客户端与服务端建立长连接,进行数据通讯。

(5)客户端与服务端启动后,后台会启动定时器,每隔一定的时间发送统计数据给monitor。

二、JDK标准的-SPI

在面向对象中,模块之间都是基于接口的,模块之间如果其中一个模块或接口实现需要进行更改,则就需要修改代码。为了实现不对模块或实现类之间不进行硬编码,即不在模块里写死代码,就可以使用spi这种服务发现机制。jdk中就提供了这种标准的spi机制,将模块实现移到代码之外。

· jdk-spi的具体约定

当服务的提供者提供了一个接口多种实现时,一般在jar包的META-INF/services/目录下,创建该接口的同名文件,该文件里的内容就是该服务接口的具体实现类的名称。

当外部加载这个模块的时候,就能通过jar包META-INF/services/里的配置文件得到具体的实现类名,并加载与实例化,完成模块之间的装配。

· jdk中使用spi的例子

mysql的Driver驱动的加载就使用到了spi机制。如下图,在META-INF中配置了Driver的实现类名为:com.mysql.cj.jdbc.Driver。

代码语言:javascript
复制
com.mysql.cj.jdbc.Driver

jdk的spi通过ServiceLoader.load进行加载。虽然Driver的接口很多,但是META-INF中配置的类名只有一个,因此只会对com.mysql.cj.jdbc.Driver实现类进行加载。

代码语言:javascript
复制
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);

· jdk标准的SPI机制的缺点

JDK标准的SPI会一次性实例化扩展点所有实现,因此不管是否用到这些实现类都会加载META-INF中配置的所有实现类,比较耗时。

dubbo内核中,也使用了spi机制,但是内部对spi的实现进行了修改来保证性能。

三、dubbo内核-SPI

· dubbo-spi的具体约定

spi文件的存储路径在META-INF/dubbo/internal目录下。

spi文件内容定义为:扩展名=具体的类型。如下图所示:

代码语言:javascript
复制
dubbo=org.apache.dubbo.registry.dubbo.DubboRegistryFactory

dubbo-spi比jdk-spi的内容定义中多了扩展名,即dubbo对spi的实现类进行加载时,会根据key即扩展名对需要用到的spi实现类进行加载。

spi实现类则是通过ExtensionLoader进行加载。

· spi实现路径

(1)getExtensionLoader获取ExtensionLoader。

可以通过ExtensionLoader.getExtensionLoader(Class<T> type)来为type接口new一个ExtensionLoader,并缓存起来。

我们可以看dubbo容器Container启动时的main方法中的关键一行代码:

代码语言:javascript
复制
public class Main {
    private static final ExtensionLoader<Container> loader             = ExtensionLoader.getExtensionLoader(Container.class);
代码语言:javascript
复制
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    //...    //判断缓存中是否存在 loader    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {        //new出一个loader,到缓存中        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}

· ExtensionLoader构造函数

代码语言:javascript
复制
private ExtensionLoader(Class<?> type) {
    this.type = type;
    objectFactory = (type == ExtensionFactory.class ? null        : ExtensionLoader.getExtensionLoader(ExtensionFactory.class)                                                .getAdaptiveExtension());
}

ExtensionLoader内部存放了type即Container接口名,与objectFactory即ExtensionFactory,后续可通过extensionFactory获取Container接口的实现。

ExtensionLoader.getExtensionLoader(Class<T> type)执行完毕,就表示Container接口的ExtensionLoader被创建了出来并被放置在了缓存中,并且ExtensionLoader内部完成了type和extensionFactory属性的初始化。

· ExtensionFactory的作用

可通过getExtension为dubbo的IOC提供所有对象。并且ExtensionFactory的objectFactory为null。

(2)getAdaptiveExtension获取扩展装饰类对象

被@Adaptive注解修饰的类就是扩展装饰类,点开注解后我们可以看到,@Adaptive注解只能修饰类和方法。

阅读getAdaptiveExtension源码:

代码语言:javascript
复制
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;
}

上述代码主要为从缓存中获取adaptiveExtension,如果不存在则创建,创建成果后再放入到缓存中。

· createAdaptiveExtension

代码语言:javascript
复制
private T createAdaptiveExtension() {
    try {
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension ");
    }
}

(1)getAdaptiveExtensionClass

代码语言:javascript
复制
代码语言:javascript
复制
private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

第一步:getExtensionClasses中会加载SPI中的实现类。

代码语言:javascript
复制
private Map<String, Class<?>> loadExtensionClasses() {
//...//加载META-INF/dubbo/internal/接口名 SPI文件中的实现类。    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName());
    return extensionClasses;
}
代码语言:javascript
复制
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
    //...    //SPI实现类 是否包含adaptive注解    if (clazz.isAnnotationPresent(Adaptive.class)) {
        if (cachedAdaptiveClass == null) {            //对cachedAdaptiveClass赋值            cachedAdaptiveClass = clazz;
            //...    //构造函数中是否包含目标接口    } else if (isWrapperClass(clazz)) {
        Set<Class<?>> wrappers = cachedWrapperClasses;
        if (wrappers == null) {
            cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
            wrappers = cachedWrapperClasses;
        }        //存在这样的实现类则放到wrappers中            wrappers.add(clazz);
    } else {
        //...
        if (names != null && names.length > 0) {            //包含activate注解的实现类存放在cachedActiviates中            Activate activate = clazz.getAnnotation(Activate.class);
            if (activate != null) {
                cachedActivates.put(names[0], activate);
            } else {
                com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
                if (oldActivate != null) {
                    cachedActivates.put(names[0], oldActivate);
                }
            }
            for (String n : names) {                //剩余的类就放在cachedNames                if (!cachedNames.containsKey(clazz)) {
                    cachedNames.put(clazz, n);
                }
                //...
            }
        }
    }
}

上述源码中会加载type-目标接口位于META-INF/dubbo/internal/接口名下的SPI实现类,并会根据对应策略将实现内容放在不同的缓存变量中。

如果实现类中包含了adapive注解修饰的类,则会把该目标接口放到cachedAdaptiveClass中,例如ExtensionFactory。

如果实现类中的构造函数中包含了目标接口,则会将实现类放到cachedWrapperClasses集合中。

如果实现类类中包含了activate注解修饰的类,则会把实现类放到cachedActivates中。

剩余其他的类,则放在cachedNames中。

代码语言:javascript
复制
private Class<?> getAdaptiveExtensionClass() {    //加载目标接口的SPI实现类
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {        //cachedAdaptiveClass在加载时被赋值了则直接返回        return cachedAdaptiveClass;
    }    //否则创建
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

(2)createAdaptiveExtensionClass

生成一个动态的adaptive类。

代码语言:javascript
复制
private Class<?> createAdaptiveExtensionClass() {
    String code = createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    org.apache.dubbo.common.compiler.Compiler compiler= ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}

这里和动态代理有点相似,生成了一个动态的adaptive类。类名则为<目标接口名>$Adaptive implements <目标接口>。

动态类中的方法,只有方法被@Adaptive修饰的方法才会实现。没有被修饰的方法则无法实现。

· injectExtension

代码语言:javascript
复制
private T injectExtension(T instance) {
    //...

Object object = objectFactory.getExtension(pt, property);

代码语言:javascript
复制
    //...
代码语言:javascript
复制
    return instance;
}

在injectExtension中进行dubbo的IOC,本质上就是从spi和spring中获取对象进行赋。

SpiExtensionFactory.getExtension和SpringExtensionFactory.getExtension。

@Adaptive注解总结

官方文档中解释为:Adaptive可注解在类或方法上。当注解在类上时,dubbo不会为该类生成代理类,表示扩展的加载逻辑由人工编码完成。当注解在方法上时,dubbo会为该方法生成代理逻辑,表示拓展的加载逻辑需由框架自动生成。

(1)注解在接口上

在调用getAdaptiveExtension方法时,直接返回该类,然后执行IOC。

(2)注解在方法上

在调用getAdaptiveExtension方法时,会生成代理类,然后执行IOC。代码模板如下:

(3)getExtension获取对象

代码语言:javascript
复制
public T getExtension(String 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;
}
代码语言:javascript
复制
private T createExtension(String name) {    //从缓存中获取class对象,即对META-INF进行加载时会放到缓存中    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {        //从缓存中获取对象        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }        //进行dubbo的IOC set注入        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);
    }
}

四、dubbo-spi实现路径总结

(1)通过getExtensionLoader获取ExtensionLoader。

(2)通过getAdaptiveExtension获取扩展装饰类对象,动态生成装饰类或动态代理类,进行依赖注入。

(3)最后可通过ExtensionLoader.getExtension(name)获取对象。

就是这么简单。

· getExtension中所用到的设计模式

(1)被@Adaptive修饰的类,使用了装饰者模式。

(2)被@Adaptive修饰的方法,生成了动态的Adaptive类,使用了动态代理模式。

(3)使用缓存确保只生成一个类,使用了单例模式。

(4)objectFactory将对象new出来的过程隐藏在工厂中,使用了工厂模式。

(5)injectExtension中使用了IOC,还有资料说试用了AOP,这个AOP我没看出来。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码云大作战 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
微服务引擎 TSE
微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档