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

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

@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类里.

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列表的代码时

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文件中, 具体如下:

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

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

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

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

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

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

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

Dubbo的Filter链梳理---分组可见和顺序调整的更多相关文章

  1. Dubbo的Filter实战--整合Oval校验框架

    前言: 其实很早之前就想写一篇关于oval和具体服务相整合的常见做法, 并以此作为一篇笔记. 趁现在项目中间空闲期, 刚好对dubbo的filter有一些了解. 因此想结合两者, 写一下既结合校验框架 ...

  2. dubbo异常filter

    dubbo请求调用过程分析 https://blog.csdn.net/javahongxi/article/details/72876694 浅谈dubbo的ExceptionFilter异常处理  ...

  3. 聊聊Dubbo(六):核心源码-Filter链原理

    转载:https://www.jianshu.com/p/6dd76ce7338f 0 前言 对于Java WEB应用来说,Spring的Filter可以拦截WEB接口调用,但对于Dubbo接口,Sp ...

  4. dubbo中的Filter链原理及应用

    转载:https://www.jianshu.com/p/f390bb88574d filter在dubbo中的应用非常广泛,它可以对服务端.消费端的调用过程进行拦截,从而对dubbo进行功能上的扩展 ...

  5. dubbo+zipkin调用链监控

    分布式环境下,对于线上出现问题往往比单体应用要复杂的多,原因是前端的一个请求可能对应后端多个系统的多个请求,错综复杂. 对于快速问题定位,我们一般希望是这样的: 从下到下关键节点的日志,入参,出差,异 ...

  6. dubbo 自定义 Filter

    通过自定义 Filter,可以在 dubbo 调用链中加入特定的逻辑,比如埋点分析调用链. 1. 新建 Filter 类 // @Activate(group = {Constants.CONSUME ...

  7. spring-cloud-Zuul学习(三)【中级篇】--Filter链 工作原理与Zuul原生Filter【重新定义spring cloud实践】

    这里开始记录zuul中级进阶内容.前面说过了,zuul主要是一层一层的Filter过滤器组成,并且Zuul的逻辑引擎与Filter可用其他基于JVM的语言编写,比如:Groovy. 工作原理 Zuul ...

  8. dubbo+zipkin调用链监控(二)

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  9. dubbo 提示No such extension Filter for filter/com.alibaba.dubbo.rpc.Filter

    配置时 <dubbo:provider filter="DubboExceptionFilter"></dubbo:provider> DubboExcep ...

随机推荐

  1. Codeforces 333E Summer Earnings - bitset

    题目传送门 传送门I 传送门II 传送门III 题目大意 给定平面上的$n$个点,以三个不同点为圆心画圆,使得圆两两没有公共部分(相切不算),问最大的半径. 显然答案是三点间任意两点之间的距离的最小值 ...

  2. 从GitHub远程仓库中删除文件夹或文件

    在上传项目到github时,忘记忽略了某个文件夹target,就直接push上去了, 最后意识到了此问题,决定删除掉远程仓库中的target文件夹 删除前: 删除后: 在github上只能删除仓库,却 ...

  3. Java IO流及应用(一)

    IO流 IO流概述及FileWriter类的使用 FileReader类使用 缓冲流介绍和使用 IO流相关案例 NO.one IO流概述及FileWriter类使用 1.1 IO流概述及分类 IO流用 ...

  4. Bumped!【最短路】(神坑

    问题 B: Bumped! 时间限制: 1 Sec  内存限制: 128 MB 提交: 351  解决: 44 [提交] [状态] [命题人:admin] 题目描述 Peter returned fr ...

  5. 使用Gitlab实现自动化部署与持续集成

    Gitlab-Ci运行原理: 由以下两个模块组成gitlab-ci servergitlab-ci-runner其中,gitlab-ci server负责调度.触发Runner,以及获取返回结果. 而 ...

  6. HTML辅助方法

    顾名思义,HTML辅助方法(HTML Helper)就是用来辅助产生HTML之用,在开发View的时候一定会面对许多HTML标签,处理这些HTML的工作非常繁琐,为了降低View的复杂度,可以使用HT ...

  7. python学习(五)

  8. 基于c#的windows基础设计(学习日记2)【关于多态】

    这次的多态很简单,没什么知识点 . 直接贴代码了: public abstract class Animal //建立一个抽象类 { private bool m_sex; private strin ...

  9. es6中...是什么意思。

    1. var set = new Set([1, 2, 3, 4, 4,4,4,4,2,2,2]) set=[...set] 2. let [head, ...tail] = [1, 2, 3, 4] ...

  10. Nginx的使用(一)Nginx+IIS实现一个网站绑定多个https域名

    使用nginx最初的目的是为了解决iis7下无法配置多个443端口的问题,iis7下不同的域名无法同时绑定443端口,据说iis8是可以的,但是iis8的话需要安装windows server2012 ...