【dubbo】 dubbo Filter 如何根据环境指定自己的Filters (其他SPI接口同理)?一起解读源码

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

  • 全局指定直接使用

    <dubbo:consumer filter="filter1,filter2"/> 
    复制代码

    就可以了;

  • 如果是service单独指定可如下配置,consumer端类似

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 也可以做业务上的自定义调用链路。
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享