Dubbo的扩展机制

2 篇文章 0 订阅
2 篇文章 0 订阅

Dubbo的扩展机制

Dubbo扩展原理
ExtensionLoader的设计和实现方式
自定义扩展例子

  • Dubbo的扩展原理

Dubbo采用的是内核+扩展的体系结构,除了Service和Config层,其他层的功能都是可扩展的(Proxy、Registry、Cluster、Monitor、Protocol、Exchange、Transport、Serialize)。
也就是说,这些层的功能模块,都可以通过配置的方式灵活地切换实现,并不需要修改框架的代码。
Dubbo 使用 URL 总线模式(包含了Key-Value)传递配置信息,所有的状态数据信息都可以从URL中解析获取。

Dubbo 自身的功能也是通过扩展点实现的,也就是 Dubbo 的所有功能点都可被用户自定义扩展所替换。
Dubbo本身也为各层提供了多种实现,比如Registry,Dubbo就提供了MulticastRegistry、RedisRegistry、ZookeeperRegistry等实现。(使用Redis作为注册中心)。

Dubbo同样支持用户自定义的扩展实现。

Dubbo的扩展点由Java的SPI扩展点发现机制扩展而来。
自定义的扩展方式可以支持延迟加载,不用每次都一次性加载出所有的配置功能;Dubbo扩展还支持以key=value的方式进行配置;Dubbo扩展还支持轻量级的依赖注入,会将扩展点实现类中的另外的扩展点引用“顺便”给初始化了。

  • ExtensionLoader的设计和实现方式
    扩展加载器,是Dubbo自定义扩展了SPI功能的加载器,它有一个getExtensionLoader方法,用来获取某个接口的ExtensionLoader,每个接口只有一个该对象。
    查看它提供的方法:

    对外方法主要有3个类别,

    activeExtension
    adaptiveExtension
    defaultExtension


除了需要提供获取扩展的API,它主要关注如下的功能:
1、在运行时灵活适配不同的扩展实现
2、能支持扩展缓存,以避免重复加载
3、能自动装配扩展的嵌套的情况 前面说过,每种功能Dubbo提供了多种实现,在使用的时候不能硬编码决定使用哪一个实现。除了支持Dubbo自身的实现以外,还需要提供对外部实现的支持,这样才能确保整个框架的灵活性。`@Adaptive` 就是为了解决这个问题而设计的(显然,一个接口不能有多个@Adaptive实现)。 1、一种情况是对某个实现进行适配。

@Adaptive 注解可以可以用来标注在某个接口的实现类上,表示这个实现并不是用来做业务逻辑处理的,而是用来适配这个接口的各种实现的。
比如:AdaptiveExtensionFactory被标注了 @Adaptive ,在调用 ExtensionLoader

ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()

方法时,将返回AdaptiveExtensionFactory实例,它用来适配SpiExtensionFactorySpringExtensionFactory实现,具体用哪个,会根据运行时的状态来确定。

@Adaptive 注解同样可以标注在接口的方法上。ExtensionLoader通过分析接口配置的adaptvie规则来动态生成adaptvie类。
@Adaptive有一个value属性,通过这个值来设置规则。由于Dubbo采取了URL总线的方式来传递数据,所以加上该注解的方法参数,需要可以获得URL对象,或者直接就是URL类型或者子类型的对象。以value的值为key,去URL里面查找。
如果查找不到,则会把类名拆分,例如:com.alibaba.dubbo.xxx.YyyInvokerWrapper将会拆分成String[] {“yyy.invoker.wrapper”},用于在URL中查找配置。
如果不能获取到,则会返回SPI标注的接口中声明的名称的扩展。

@Adaptive接口标注的实现,是一个适配器

2、另一种情况是Dubbo框架动态生成适配器类
如果没有找到@SPI接口的@Adaptive实现,ExtensionLoader会动态创建适配器类。
ExtensionLoader通过分析接口配置的adaptive规则,动态生成类,并且加载到ClassLoader中。
该注解有一个value属性,通过这个属性,可以设置该接口的adaptive规则,所以结合URL总线设计,参数都需要从URL中获取。
所以这样的方法都需要提供URL对象作为参数。
以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;
}
这个接口是一个扩展接口,默认扩展名为`netty`。它有两个方法, 一个是bind,提供URL参数,注解说明,它是配置服务端的transporter功能; 一个是connect,提供URL参数,注解说明,它是配置客户端的transporter功能; 它没有Adaptive实现,但是它的方法提供了URL参数。 ![](http://owu0nfc62.bkt.clouddn.com/transporter.png) `ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();` 的时候,按如下调用链 `getAdaptiveExtension` -> `createAdaptiveExtension` -> `getAdaptiveExtensionClass` -> `createAdaptiveExtensionClass` -> `createAdaptiveExtensionClassCode` `createAdaptiveExtensionClassCode`方法,将会构造一个临时的Adaptive类文件(当然,它需要实现扩展接口,例如Transporter),名字为`接口名+$Adaptive` 关于接口方法的实现,它将从URL中获取接口方法注解上的参数,Transporter接口中,如:Constants.SERVER_KEY, Constants.TRANSPORTER_KEY。 用于构造Transporter实现,并且最终生成如下的代码:
package com.alibaba.dubbo.remoting;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adpative implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
}
}
这样一来,除了参数获取方式不一样。其实和自己定义的Adaptive实现没什么区别了。 **拿到Adaptive(其实这就是一个代理工厂),并在代理工厂内屏蔽实现的差异(获取到准确的扩展实现,然后调用方法,最后返回)** 扩展灵活配置,还体现在另一个注解上`@Activate`。这个注解使用在接口的实现类上,用来标注使用这个实现的前提条件。 比如:`ValidationFilter`被标注为:
@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.VALIDATION_KEY, order = 10000)
public class ValidationFilter implements Filter {...}
表明这个Filter实现,在做Validation的时候,可以在客户端和服务端生效; value表示另外一个激活条件,注意上面截图的方法,active extension相关的方法,需要提供一个URL参数, 那么这个value配置的值,表示URL中必须要有指定的参数才能激活这个扩展。 order值越大越靠前。 `这里指的是对 框架本身对接口的实现 的排序,用户扩展的实现,将放置在后面。` Dubbo中对该注解用得最多的地方是`Filter`的实现。Dubbo的调用经常会使用过滤器链的形式,哪些实现以及实现的顺序,是由`@Activate`注解来控制的。
  • 自定义扩展的例子
    目标:引入Dubbo框架,自定义扩展,用ExtensionLoader检验Dubbo框架是否能正确解析配置,并且返回正确结果。
    项目结构搭建省略。
    1、定义扩展接口:
@SPI("default")
public interface MyExtension {
    @Adaptive
    String sayHello(String name, String extensionType);
}

它的方法没有提供URL,所以,只能显式定义一个Adaptive类。

2、定义两个实现:

public class DefaultExtension implements MyExtension {
    @Override
    public String sayHello(String name, String extensionType) {
        return "This is DEFAULT implementation, and hello - "+name;
    }
}
public class SWExtension implements MyExtension {
    @Override
    public String sayHello(String name, String extensionType) {
        return "This is SW implementation, and hello - "+name;
    }
}

3、定义Adaptive类,根据类型获取实现。

@Adaptive
public class AdaptiveExtension implements MyExtension {
    public String sayHello(String name, String extensionType) {
        ExtensionLoader<MyExtension> loader = ExtensionLoader.getExtensionLoader(MyExtension.class);
        MyExtension extension = loader.getDefaultExtension();
        switch (extensionType){
            case "default":
                extension = loader.getExtension("default");
                break;
            case "sw":
                extension = loader.getExtension("sw");
                break;
            default:
                break;
        }
        return extension.sayHello(name,extensionType);
    }
}

4、配置META-INF/dubbo/cn.irving.extension.MyExtension

default=cn.irving.extension.DefaultExtension
sw=cn.irving.extension.SWExtension
adaptive=cn.irving.extension.AdaptiveExtension

5、定义测试类

public class ExtensionTest {
    public static void main(String[] args) {
        MyExtension extension = ExtensionLoader.getExtensionLoader(MyExtension.class).getAdaptiveExtension();
        System.out.println(extension.sayHello("Irving","sw"));
// ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }
}

运行结果:

This is SW implementation, and hello - Irving

参考资料:
http://dubbo.io/books/dubbo-dev-book/SPI.html
https://my.oschina.net/bieber/blog/418949

-EOF-

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值