• Dubbo的Filter链梳理---分组可见和顺序调整


    转载:https://www.cnblogs.com/mumuxinfei/p/9231310.html

    前言:
      刚刚写了篇博文: Dubbo透传traceId/logid的一种思路, 对dubbo的filter机制有了一个直观的理解. 同时对filter也多了一些好奇心, 好奇filter链是如何组织的, 它的顺序是否支持调整. 带着这些疑问, 同时也是趁热打铁, 让我们一起来简单梳理下.

    写下疑惑:
      其实网上有一篇文章: Dubbo Filter详解, 写的非常好, 基本上把Dubbo的Filter链的组织, 顺序性, 自定义顺序的方式, 说的很清楚了. 这边请允许我再做一次知识的搬运工, ^_^.
      同时让我引入几个疑问, 带着疑问去解读代码, 可能效果更好.
      1. 注解@Activate是否是Dubbo Filter必须的, 其上的group和order分别扮演什么样的角色?
      让我们贴一下ConsumerContextFilter的类定义

    1
    2
    3
    4
    5
    6
    7
    @Activate(
        group = {"consumer"},
        order = -10000
    )
    public class ConsumerContextFilter implements Filter {
     
    }

      2. Filter的顺序是否可以调整, 如何实现?
      这里面又可以分好几个小问题, 比如默认的filter是按什么标准排序的, 如何调整自定义filter和自带filter的顺序, 甚至去掉自带filter.
      在Dubbo透传TraceId的实践中, 就发现自定义的filter是在系统自带filter后执行的, 但是我想调整顺序, 却发现无从入手, T_T.

    源码解读:
      Dubbo的Filter链构造的入口是在ProtocolFilterWrapper类里.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    public class ProtocolFilterWrapper implements Protocol {
     
        private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
             
            final Invoker last = invoker;
            // *) 获得所有激活的Filter(已经排好序的)
            List filters = ExtensionLoader.getExtensionLoader(Filter.class)
                    .getActivateExtension(invoker.getUrl(), key, group);
     
            if(filters.size() > 0) {
               // *) 以非常规的方式--闭包, 构建了filter链
                for(int i = filters.size() - 1; i >= 0; --i) {
                    final Filter filter = (Filter)filters.get(i);
                    last = new Invoker() {
                        public Class<T> getInterface() {
                            return invoker.getInterface();
                        }
     
                        public URL getUrl() {
                            return invoker.getUrl();
                        }
     
                        public boolean isAvailable() {
                            return invoker.isAvailable();
                        }
     
                        public Result invoke(Invocation invocation) throws RpcException {
                            return filter.invoke(last, invocation);
                        }
     
                        public void destroy() {
                            invoker.destroy();
                        }
     
                        public String toString() {
                            return invoker.toString();
                        }
                    };
                }
            }
     
            return last;
        }
     
    }

      而在具体的获取激活的filter列表的代码时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    public List<T> getActivateExtension(URL url, String[] values, String group) {
        ArrayList exts = new ArrayList();
        // 所有用户自己配置的filter信息(有些Filter是默认激活的,有些是配置激活的,这里这里的names就指的配置激活的filter信息)
        Object names = values == null?new ArrayList(0):Arrays.asList(values);
        String name;
        // 配置指定的项包含'-default'时, 则不加载默认的filter链组, 反之则加载
        if(!((List)names).contains("-default")) {
            this.getExtensionClasses();
            Iterator usrs = this.cachedActivates.entrySet().iterator();
     
            while(usrs.hasNext()) {
                Entry i = (Entry)usrs.next();
                name = (String)i.getKey();
                Activate ext = (Activate)i.getValue();
                // group的取值范围限于provider/consumer, 标明作用的场景
                if(this.isMatchGroup(group, ext.group())) {
                    Object ext1 = this.getExtension(name);
                    // 这里以Filter为例:三个判断条件的含义依次是:
                    // 1.用户配置的filter列表中不包含当前ext
                    // 2.用户配置的filter列表中不包含当前ext的加-的key
                    // 3.如果用户的配置信息(url中体现)中有可以激活的配置key并且数据不为0,false,null,N/A,也就是说有正常的使用
                    if(!((List)names).contains(name) && !((List)names).contains("-" + name) && this.isActive(ext, url)) {
                        exts.add(ext1);
                    }
                }
            }
            // 根据Activate注解上的order排序, 有点类似css的zindex属性, 数值越小排在前面
            Collections.sort(exts, ActivateComparator.COMPARATOR);
        }
        // 进行到此步骤的时候Dubbo提供的原生的Filter已经被添加完毕了,下面处理用户自己扩展的Filter
        ArrayList var11 = new ArrayList();
     
        for(int var12 = 0; var12 < ((List)names).size(); ++var12) {
            name = (String)((List)names).get(var12);
            // 该项不是 剔除项(以'-'开头)时, 进入添加环节
            if(!name.startsWith("-") && !((List)names).contains("-" + name)) {
                // 可以通过default关键字替换Dubbo原生的Filter链,主要用来控制调用链顺序
                if("default".equals(name)) {
                    if(var11.size() > 0) {
                        // 加入用户自己定义的扩展Filter
                        exts.addAll(0, var11);
                        var11.clear();
                    }
                else {
                    Object var13 = this.getExtension(name);
                    var11.add(var13);
                }
            }
        }
     
        if(var11.size() > 0) {
            exts.addAll(var11);
        }
     
        return exts;
    }

      通过简单的配置'-'可以手动剔除Dubbo原生的Filter通过default代表Dubbo原生的Filter子链, 通过配置指定从而实现filter链的顺序控制. 这大概这段代码可以解读出的核心思想.

      默认filter链, 先执行原生filter, 再依次执行自定义filter, 继而回溯到原点.

      Dubbo原生的filter定义在META-INF/dubbo/internal/com.alibaba.dubbo.rpc.filter文件中, 具体如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    echo=com.alibaba.dubbo.rpc.filter.EchoFilter
    generic=com.alibaba.dubbo.rpc.filter.GenericFilter
    genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
    token=com.alibaba.dubbo.rpc.filter.TokenFilter
    accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
    activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
    classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
    context=com.alibaba.dubbo.rpc.filter.ContextFilter
    consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
    exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
    executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
    deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
    compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
    timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
    monitor=com.alibaba.dubbo.monitor.support.MonitorFilter
    validation=com.alibaba.dubbo.validation.filter.ValidationFilter
    cache=com.alibaba.dubbo.cache.filter.CacheFilter
    trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
    future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter

    解答疑惑:
      通过源码的解读, 我们来尝试回答一下开头设定的两个问题.
      1. 注解@Activate是否是Dubbo Filter必须的, 其上的group和order分别扮演什么样的角色?
      对于Dubbo原生自带的filter, 注解@Activate是必须, 其group用于provider/consumer的站队, 而order值是filter顺序的依据. 但是对于自定义filter而言, 注解@Activate没被用到, 其分组和顺序, 完全由用户手工配置指定. 如果自定义filter添加了@Activate注解, 并指定了group了, 则这些自定义filter将升级为原生filter组.
      2. Filter的顺序是否可以调整, 如何实现?
      可以调整, 通过'-'符号可以去除某些filter, 而default代表默认激活的原生filter子链, 通过重排default和自定义filter的顺序, 达到实现顺序控制的目的.

    案例实战:
      让我们来构建几个case, 来看看如何配置能满足.
      假定自定义filter的对象为filter1, filter2
      case 1: 其执行顺序为, 原生filter子链->filter1->filter2

    1
    <dubbo:reference filter="filter1,filter2"/>

      case 2: 其执行顺序为, filter1->filter2->原生filter子链

    1
    <dubbo:reference filter="filter1,filter2,default"/>

      case 3: 其执行顺序为, filter1->原生filter子链->filter2, 同时去掉原生的TokenFilter(token)

    1
    <dubbo:service filter="filter1,default,filter2,-token"/>

    总结:
      回过头来看, Dubbo越来越像一个精品, 其设计的filter机制(分组/顺序控制), 非常的巧妙, 值得拜读和学习.

  • 相关阅读:
    ModelFirst的CRUD
    EF中逆变和协变
    ERP客户关系渠管理(二十)
    ERP反馈信息管理(十九)
    ERP系统上传文档信息下载(十八)
    比较 数组与对象的同源特性
    对android的认识
    我的知乎
    HTTP来源地址
    BroadcastReceiver 翻译
  • 原文地址:https://www.cnblogs.com/ceshi2016/p/12016141.html
Copyright © 2020-2023  润新知