Spring Dubbo开发笔记(三)——dubbo扩展

完整的代码及:

github: https://github.com/Athlizo/spring-dubbo-parent

码云: https://git.oschina.net/null_584_3382/spring-dubbo-parent

1.概述:

dubbo框架中,提供了多种扩展,比如Dubbo的过滤器扩展,路由扩展等等。并且dubbo已经提供了扩展的一些默认实现。本篇文章主要介绍:1)dubbo扩展原理,2)通过简单的改造,使dubbo让扩展的使用更方便。

2. dubbo扩展

2.1 怎么创建dubbo扩展

以拦截器作为例子说明,引用dubbo官方文档中的一个图。

<!-- 在xml配置文件中设置 -->
<dubbo:reference filter="xxx,yyy" /> <!-- 消费方调用过程拦截 -->
<dubbo:consumer filter="xxx,yyy"/> <!-- 消费方调用过程缺省拦截器,将拦截所有reference -->
<dubbo:service filter="xxx,yyy" /> <!-- 提供方调用过程拦截 -->
<dubbo:provider filter="xxx,yyy"/> <!-- 提供方调用过程缺省拦截器,将拦截所有service -->
dubbo扩展配置文件
src
 |-main
    |-java
        |-com
            |-xxx
                |-XxxFilter.java (实现Filter接口)
    |-resources
        |-META-INF
            |-dubbo
                |-com.alibaba.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter)
//扩展类
package com.xxx;
 
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
 
 
public class XxxFilter implements Filter {
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        // before filter ...
        Result result = invoker.invoke(invocation);
        // after filter ...
        return result;
    }
}

 

  1. 第一步是配置Dubbo Filter,有两种方法,第一种是在配置文件里面加入相关扩展配置,例如<dubbo:provider filter="xxx"/>。第二种方法是针对集合类扩展,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker等,可以同时加载多个实现,使用@Activate的方式自动激活来简化配置,如:    
    
    import com.alibaba.dubbo.common.extension.Activate;
    import com.alibaba.dubbo.rpc.Filter;
     
    @Activate(group = "provider", value = "xxx") // 只对提供方激活,group可选"provider"或"consumer"
    public class XxxFilter implements Filter {
        // ...
    }

     

  2. 第二步是创建配置文件,在resource/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter文件里面写配置xxx=com.xxx.XxxFilter
  3. 第三步是创建对应的Class,在配置对应的包下面新建一个XxxFilter,实现Dubbo的Filter接口。

通过以上3步就可以自定义一个Filter

2.2 Dubbo是怎么加载扩展

2.1.1 ExtensionLoader<T>

1) 创建:

这个类是用户管理所有的dubbo扩展,是一个泛型,根据不同的扩展类(例如Filter,Protocol等),保存该类扩展的所有实现类。

例如,要想获取每个具体的ExtensionLoader,使用getExtensionLoader(Class) 来获取,如果没有就创建,如果之前创建过就返回之前创建的,逻辑比较简单。

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    ... //省略参数校验
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        //EXTENSION_LOADERS用于保存ExtensionLoader所有泛型实现子类
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}
2)获取:

获取有效的扩展类,是通过getActivateExtension方法。代码如下:

public List<T> getActivateExtension(URL url, String[] values, String group) {
    List<T> exts = new ArrayList<T>();
    List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
    if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
        //加载扩展扩展的类
        getExtensionClasses();
        //加载@Activate注解的配置的扩展
        for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
            String name = entry.getKey();
            Activate activate = entry.getValue();
            if (isMatchGroup(group, activate.group())) {
                T ext = getExtension(name);
                if (! names.contains(name)
                        && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name) 
                        && isActive(activate, url)) {
                    exts.add(ext);
                }
            }
        }
        Collections.sort(exts, ActivateComparator.COMPARATOR);
    }
    List<T> usrs = new ArrayList<T>();
    //加载 通过values传递过来的指定扩展
    for (int i = 0; i < names.size(); i ++) {
       String name = names.get(i);
        if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX)
              && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
           if (Constants.DEFAULT_KEY.equals(name)) {
              if (usrs.size() > 0) {
              exts.addAll(0, usrs);
              usrs.clear();
              }
           } else {
           T ext = getExtension(name);
           usrs.add(ext);
           }
        }
    }
    if (usrs.size() > 0) {
       exts.addAll(usrs);
    }
    return exts;
}

传入的参数说明:

  • url-我们知道dubbo的RPC调用相关信息都是通过URL形式保存的,因此url参数即是当前的某个服务调用
  • values-用来指定加载某些特殊的扩展,例如通过<dubbo:provider filter="xxx"/>来配置的过滤器,则在url中会有相关信息(service.filter),然后加载指定过滤器
  • group-用来处理@Activate注解的方式配置扩展

其中有一个比较重要的方法,getExtensionClasses(),原理就是初始化扩展名字及其对应的Class,在往里面看

private Map<String, Class<?>> getExtensionClasses() {
       Map<String, Class<?>> classes = cachedClasses.get();
       if (classes == null) {
           synchronized (cachedClasses) {
               classes = cachedClasses.get();
               if (classes == null) {
                   classes = loadExtensionClasses();
                   cachedClasses.set(classes);
               }
           }
       }
       return classes;
}

其中loadExtensionClasses方法如下:这里看到了,为什么要配置在自定义过滤器的时候我们需要配置META-INFO.dubbo.com.alibaba.dubbo.rpc.Filter这个文件,原来就是在这里通过文件加载到ExtensionLoad中去的,并且路径写死在这里。

private Map<String, Class<?>> loadExtensionClasses() {
    ...
    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    // META-INF/dubbo/internal/
    loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    // META-INF/dubbo/
    loadFile(extensionClasses, DUBBO_DIRECTORY);
    // META-INF/services/
    loadFile(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
}
3) 使用

还是以Filter为例子,在哪里调用Filter呢?首先要明白,dubbo的远程调用都是通过抽象接口Invoker为核心。ProtocolFilterWrapper的类中的buildInvokerChain方法用来创建Invoder的调用链。核心代码如下:

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    if (filters.size() > 0) {
        for (int i = filters.size() - 1; i >= 0; i --) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {
                ...
                public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation);
                }
               ...
            };
        }
    }
    return last;
}

这里就使用到了ExtensionLoader的getActivateExtension方法获取当前有效的Filter。

3. Spring Boot Style

在 https://my.oschina.net/u/3039671/blog/856577 文章中介绍了怎么使用Spring boot来加载dubbo的相关bean,那么就会想,对于dubbo的Filter,有没有更优雅的声明方式?例如Spring MVC中的声明一个Filter就是直接声明一个普通的Bean一样。

还是以Filter为例子(注意,下面分析Filter是针对全局的Filter,即Provider或者Consumer层面Filter)

首先,扩展是从ExtensionLoad<Filter>中获取,那么我们的目的就是在ExtensionLoad<Filter>中加入自己的Filter。上面说过,ExtensionLoad加载扩展有2种方式,一种是通过参数中Values来获取,另外一种是通过@Activate注解。简单分析一下:

使用Values参数的方式

  1. 需要在声明一个自己的Filter的时候,同时必须创建一个ProviderConfig或者ConsumerConfig(以代替<dubbo:provider filter="xxx"/>这样的配置)
  2. 需要手动往ExtensionLoad中加入我们自己的Filter(以代替通过读取com.alibaba.dubbo.rpc.Filter配置加入到ExtensionLoad)

使用@Activate方式

  1. 使用@Activate的话,也要往ExtensionLoad中加入我们自己的Filter,但是不用创建ProviderConfig或者ConsumerConfig。
  2. 必须要在自己的Filter上有@Activate注解。

这两种方式都能达成我们的需求,但是从开发难度来说使用@Activate注解相对简单,而且第二点可以通过其他方式(proxy代理)来解决。因此下下面介绍基于@Activate注解来快速创建一个Dubbo的Filter。

3.1 实现方式

    自定义一个BeanPostProcessor,对每一个Bean做以下处理:

190228_Lt7e_3039671.png

3.2 完成以后的效果

这样就可以快速创建一个Dubbo Filter

@Bean
    ProviderFilter providerFilter(){
        return new ProviderFilter();
    }

    static class ProviderFilter extends AbstractDubboProviderFilterSupport {
        public Result invoke(Invoker<?> invoker, Invocation invocation) {
            System.out.println("ProviderFilter");
            return invoker.invoke(invocation);
        }
    }

如果有跟定制化的需求,可以使用@Activate注解。

@Bean
    CustomFilter customFilter(){
        return new CustomFilter();
    }

    @Activate(group = Constants.PROVIDER)
    static class CustomFilter extends AbstractDubboFilterSupport {
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            System.out.println("CustomFilter");
            return invoker.invoke(invocation);
        }

        public Filter getDefaultExtension() {
            return this;
        }
    }

 

注意,工程中的AnnotationBeanPostProcessor的其实是对Dubbo中的AnnotationBean的修改,除了扫描Dubbo的@Service的注解方式修改了以外,主要是对远程代理延时创建的逻辑。否则在@Reference创建代理的时候,我们的Filter还没创建,自然Filter就不能加入到Invoker调用链中。

转载于:https://my.oschina.net/u/3039671/blog/869544

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值