1 先说解决方案(2.7.3 版本)

  • 全局指定直接使用
<dubbo:consumer filter="filter1,filter2"/>
  • 如果是service单独指定可如下配置,consumer端类似

dubbo filter扩展 dubbo自定义filter_dubbo filter扩展

2 为什么?一起来看看,filter过滤链的构造过程(2.7.3 版本)

  • 执行流程是:
  • org.apache.dubbo.config.ServiceConfig 类加载时jvm 实例化 final protocol
  • --> private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); // 类加载时执行
  • --> org.apache.dubbo.common.extension.ExtensionLoader // new 关键字实例化 jvm 执行用户自定义构造方法
  • --> org.apache.dubbo.common.extension.ExtensionLoader#ExtensionLoader
  • --> ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension() // 下面会 对SPI相关的文件对应的类进行加载并缓存
  • --> org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtension
  • --> org.apache.dubbo.common.extension.ExtensionLoader#createAdaptiveExtension
  • --> org.apache.dubbo.common.extension.ExtensionLoader#getAdaptiveExtensionClass // 加载自适应扩展类
  • --> org.apache.dubbo.common.extension.ExtensionLoader#getExtensionClasses
  • --> org.apache.dubbo.common.extension.ExtensionLoader#loadExtensionClasses // load 同步执行
  • --> org.apache.dubbo.common.extension.ExtensionLoader#loadExtensionClasses
  • --> org.apache.dubbo.common.extension.ExtensionLoader#loadDirectory // 加载目录文件
  • // 主要涉及三个目录
  1. private static final String SERVICES_DIRECTORY = "META-INF/services/";
  2. private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
  3. private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
  • // 而默认的所有过滤器都在/META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter 里面 2.7.3 默认配置如下
1 cache=org.apache.dubbo.cache.filter.CacheFilter
  2 validation=org.apache.dubbo.validation.filter.ValidationFilter
  3 echo=org.apache.dubbo.rpc.filter.EchoFilter^M
  4 generic=org.apache.dubbo.rpc.filter.GenericFilter^M
  5 genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter^M
  6 token=org.apache.dubbo.rpc.filter.TokenFilter^M
  7 accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter^M
  8 activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter^M
  9 classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter^M
 10 context=org.apache.dubbo.rpc.filter.ContextFilter^M
 11 consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter^M
 12 exception=org.apache.dubbo.rpc.filter.ExceptionFilter^M
 13 executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter^M
 14 deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter^M
 15 compatible=org.apache.dubbo.rpc.filter.CompatibleFilter^M
 16 timeout=org.apache.dubbo.rpc.filter.TimeoutFilter
 17 trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
 18 future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
 19 monitor=org.apache.dubbo.monitor.support.MonitorFilter
  • --> org.apache.dubbo.common.extension.ExtensionLoader#loadDirectory
  • --> org.apache.dubbo.common.extension.ExtensionLoader#loadResource
  • --> org.apache.dubbo.common.extension.ExtensionLoader#loadClass
  • --> org.apache.dubbo.common.extension.ExtensionLoader#cacheAdaptiveClass // 该方法缓存所有 org.apache.dubbo.common.extension.Adaptive 注解的类,包含你自定的类。
  • // 小结: 上面完成了所有SPI类型相关的缓存。下面就是服务暴露或者引用时,如何抉择Filter 去留的流程了。
  • // 决定一个filter能否生效 org.apache.dubbo.common.extension.Activate#value 默认为空,需要一个抓手:org.apache.dubbo.common.extension.ExtensionLoader#isActive 该方法校验 value 为空的话就会默认生效;否则去url中匹配该value(url中的key),如果没有或者key对应的value为空则不能生效该filter。
private boolean isActive(String[] keys, URL url) {
        if (keys.length == 0) {
            return true;
        }
        for (String key : keys) {
            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
                String k = entry.getKey();
                String v = entry.getValue();
                if ((k.equals(key) || k.endsWith("." + key))
                        && ConfigUtils.isNotEmpty(v)) {
                    return true;
                }
            }
        }
        return false;
    }
  • // Filter总的选择流程:先加载默认Activate和自定义的,但排除xml或者注解中指定的。一般在org/apache/dubbo/dubbo/2.7.3/dubbo-2.7.3.jar!/META-INF/dubbo/internal包下定义好的。
  • 再加载xml中指定的filters; 此处一定要注意,如果优选在的加载自己定义的filter必须给filter Activate注解指定value值如下:
  • @Activate(group = {CONSUMER, PROVIDER}, order = 100, value = "commonxxFilter"),然后在配置文件或者xml中自行指定该Filter的Id ;;的确有点别扭👀☠️
     
  • // 使用举例:
  • 如果注解中指定了value,则需要在url中出现该key并且指定value时才会被加到过滤链中。但是如果xml中指定,就会忽略此value值

3 最后总结:

  • 1 全局指定直接使用 就可以了,同时也可以对具体的service,reference进行覆盖;
  • 2 其他SPI接口可以做类似的配置;
  • 3 filter能否生效主要看org.apache.dubbo.common.extension.Activate 注解的value group等 具体匹配
  • 4 由此可以对不同的环境进行拦截,如果结合路由或者loadbalance SPI 也可以做业务上的自定义调用链路。