Dubbo-Dubbo SPI 扩展机制

Dubbo SPI 扩展机制

一、理解

我的理解就是SPI扩展机制给予Dubbo带来了更多的灵活的扩展性,可以自动根据URL中的参数,进行选择合适的扩展类进行处理,除了这个简单的Adapter 帮助适配之外,还引入了IOC机制,set and 构造函数;使用URL参数进行选择合适的扩展为默认的Adapter,还可以通过自己手动的Adaptive 这个注解进行自定义呢。功能十分的丰富,代码也不是非常的多,按需加载特定的SPI的扩展,性能不是问题。

spi :可以理解为,一个接口有多个实现类,我们需要根据某种策略进行选择具体的实现,Dubbo中主要根据URL 参数进行传递,比如DynamicConfigurationFactory 动态配置中心,有Etcd、Nacos、Zookeeper的实现,这里是根据协议的类型去选择一个具体的实现

直接根据某个key 去或者扩展

1、根据key 获取扩展

 DynamicConfigurationFactory factories = ExtensionLoader
                .getExtensionLoader(DynamicConfigurationFactory.class)
                .getExtension(url.getProtocol());

2、根据一个适配类,适配类根据参数进行动态获取扩展

如下获取一个适配类
ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

然后根据URL的参数,类似根据key获取扩展。

public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0, arg1);
}

org.apache.dubbo.rpc.protocol.nativethrift.ThriftProtocolTest#testThriftProtocol

  @Test
    public void testThriftProtocol() throws TException{
        DemoServiceImpl server = new DemoServiceImpl();
        ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
        Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
        URL url = URL.valueOf(org.apache.dubbo.rpc.protocol.nativethrift.ThriftProtocol.NAME + "://127.0.0.1:5341/" + DemoService.Iface.class.getName() + "?version=1.0.0&nativethrift.overload.method=true");
        Exporter<DemoService.Iface> exporter = protocol.export(proxyFactory.getInvoker(server, DemoService.Iface.class, url));
        Invoker<DemoService.Iface> invoker = protocol.refer(DemoService.Iface.class, url);
        DemoService.Iface client = proxyFactory.getProxy(invoker);
        String result = client.sayHello("haha");
        Assertions.assertTrue(server.isCalled());
        Assertions.assertEquals("Hello, haha", result);
        invoker.destroy();
        exporter.unexport();
    }

二、如何学习 Dubbo SPI

1、dubbo sample & 源码

git clone   https://github.com/apache/dubbo-samples.git

2、断点跟踪 & 源码测试用例

建议:源码跟踪毕竟代码量比较大,充足的时间、充足的经历、然后加上注释,不要害怕。

三、Dubbo SPI 关键解析

1、@SPI

注解SPI 意思就是说,这个类要被SPI 处理。

2、@Active

Active 可以理解为激活的意思,getExtension(name)是根据配置文件中定义的key 获取到唯一的Class,而Active可以理解为筛选的意思,这么多的扩展,可以根据分组可values去筛选出所有的扩展,这个是赛选多个的意思,然后在根据getExtension(name)或者所有的扩展的实例。image.png
List getActivateExtension(URL url, String[] values, String group)
image.png

3、@Adaptive

Dubbo SPI之Adaptive详解

3.1 默认适配类

我的理解是根据参数动态的选择某种扩展,默认情况下定义的SPI 会生成一个默认的扩展,这个扩展是根据URL中的参数进行选择的,也可以自己声明一个扩展哦。@Adaptive 注解只是针对适配类有效,标注到类上标明这个类就是一个适配不使用默认的,其他的就是使用默认的,使用默认的最少要有一个@Adaptive({PROXY_KEY}),注解在方法上,根据参数动态的获取到适配的SPI的实现类。

 Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();


    /**
     * create proxy.
     *
     * @param invoker
     * @return proxy
     */
    @Adaptive({PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;

断点设置到这里会看的自动生成的代码;
org/apache/dubbo/common/extension/ExtensionLoader.java:996

package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {
public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0, arg1);
}
public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
if (arg2 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
}

3.2 自定义适配类

自定义的@Adaptive,在Dubbo中就有一个AdaptiveExtensionFactory,他的策略是根据先后顺序,找到了就返回,就两种情况。

**
 * AdaptiveExtensionFactory  依赖注入工厂
 */
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        // 依赖注入 根据第一匹配原则去查找,如果SPI中有 使用 SpiExtensionFactory,如果Spring 容器中有 使用Spring 容器中的!
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

3.3 不适用自定义适配类

还有不使用@Adaptive DynamicConfigurationFactory动态配置中心,接口上没有定义动态适配,只能根据名称进行一个个的获取,不能调用getAdaptiveExtension这个方法,这里会触发自动生成适配默认的适配类的,接口上并没有定义,因此会报错
org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator#generate

image.png

 DynamicConfigurationFactory factories = ExtensionLoader
                .getExtensionLoader(DynamicConfigurationFactory.class)
                .getExtension(url.getProtocol());
/**
 * 默认是什么都没有实现的
 */
@SPI("nop")
public interface DynamicConfigurationFactory {

    /**
     * 根据SPI动态获取配置中心
     * @param url
     * @return
     */
    DynamicConfiguration getDynamicConfiguration(URL url);

}

4、@DisableInject

不允许进行依赖注入,依赖注入可以分为IOC的set 、构造依赖注入,标注这个表明不进行依赖注入处理。
假设协议是Zookeeper,那么这个ZookeeperTransporter是怎么注入依赖的?

DynamicConfigurationFactory factories = ExtensionLoader
                .getExtensionLoader(DynamicConfigurationFactory.class)
                .getExtension(url.getProtocol());

org.apache.dubbo.common.extension.ExtensionLoader#createExtension

image.png

4.1 这里是Adaptive中的注入工厂处理。

org.apache.dubbo.common.extension.ExtensionLoader#injectExtension
image.png

4.2 根据Class 类名称获取AdaptiveExtension
/**
 * SpiExtensionFactory  处理相关的依赖
 */
public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            
            //依赖注入必须实现 Adaptive 
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}
public class ZookeeperDynamicConfigurationFactory extends AbstractDynamicConfigurationFactory {

    /**
     * Zookeeper 也是动态生成的扩展
     */
    private ZookeeperTransporter zookeeperTransporter;

    public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
        this.zookeeperTransporter = zookeeperTransporter;
    }


    @Override
    protected DynamicConfiguration createDynamicConfiguration(URL url) {
        return new ZookeeperDynamicConfiguration(url, zookeeperTransporter);
    }
}

@SPI("curator")
public interface ZookeeperTransporter {

    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    ZookeeperClient connect(URL url);

}

四、源码流程解析

一个SPI扩展对应一个ExtensionLoader,所有的ExtensionLoader都有ExtensionFactory依赖注入处理工厂,一个SPI对应一个Adaptive(可能是默认的、或者自定义的)

1、无需Adaptive,直接根据key进行获取。

DynamicConfigurationFactory factories = ExtensionLoader
                .getExtensionLoader(DynamicConfigurationFactory.class)
                .getExtension(url.getProtocol());

在这里插入图片描述

2、获取Adaptive

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

在这里插入图片描述

3、获取@Active getActivateExtension

比如:Filter过滤的时候需要获取某种分组情况下的Filter ,这个服务提供者,还是服务消费者提供的呢?因此就有了分组的概念。

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

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值