Dubbo过滤器概述
Dubbo中的过滤器和Web应用中的过滤器的概念是一样的,提供了在服务调用前后插入自定义逻辑的途径。过滤器是整个Dubbo框架中非常重要的组成部分,Dubbo中很多功能都是基于过滤器扩展而来的。过滤器提供了服务提供者和消费者调用过程的拦截,即每次都执行RPC调用的时候,对应的过滤器都会生效。虽然过滤器的功能强大,但由于每次调用时都会执行,因此在使用的时候需要注意它对性能的影响。
过滤器的使用
- 一种方式是使用@Active注解默认启用;
- 一种方式是在配置文件中配置;
<!-- 消费方调用过程拦截 -->
<dubbo:reference filter="xxx, yyy" />
<!-- 消费方调用过程默认拦截器,将拦截所有reference -->
<dubbo:consumer filter="xxx, yyy" />
<!-- 服务提供方调用过程拦截 -->
<dubbo:service filter="xxx, yyy" />
<!-- 服务提供方调用过程默认拦截器,将拦截所有service -->
<dubbo:provider filter="xxx, yyy" />
配置上的“潜规则”:
- 过滤器顺序:
- 用户自定义的过滤器的顺序默认会在框架内置过滤器之后,可以使用
filter="xxx, default"
这种配置方式让自定义的过滤器顺序靠前。 - 我们在配置
filter="xxx, yyy"
时,写在前面的xxx会比yyy的顺序要靠前。
- 剔除过滤器。对于一些默认的过滤器或自动激活的过滤器,有些方法不想使用这些过滤器,可以使用
"-"
加过滤器名称来过滤,如filter="-xxFilter"
会让xxFilter不生效。如果不想使用所有默认启用的过滤器,则可以配置filter="-default"
来进行剔除。 - 过滤器的叠加。如果服务提供者、消费者端都配置了过滤器,则两边的过滤器不会相互覆盖,而是互相叠加,都会生效。如果需要覆盖,则可以在消费方使用
"-"
的方式剔除对应的过滤器。
过滤器的总体结构
所有的内置过滤器中除了CompatibleFilter特别突出,只继承了Filter接口,既不会被默认激活,其他的内置过滤器都使用了Activate注解,即默认被激活。Filter接口上有@SPI注解,说明过滤器是一个扩展点,用户可以基于这个扩展点接口实现自己的过滤器。
所有的过滤器会被分为消费者和服务提供者两种类型,消费者类型的过滤器只会在服务引用时被加入Inoker,服务提供者类型的过滤器只会在服务暴露的时候被加入对应的Invoker。MonitorFilter比较特殊,它会同时在暴露和引用被加入Invoker。
过滤器名 | 作用 | 使用方 |
AccessLogFilter | 打印每一次请求的访问日志。如果需要访问的日志只出现在指定的appender中,则可以在log的配置文件中配置additivity | 服务提供者 |
ActiveLimitFilter | 用于限制消费者端对服务的最大并行调用数 | 消费者 |
ExecuteLimitFilter | 同上,用于限制服务端的最大并行调用数」服务提供者 | |
ClassLoaderFilter | 用于切换不同线程的类加载器,服务调用完成后会还原回去 | 服务提供者 |
CompatibleFilter | 用户使返回值与调用程序的对象版本兼容,默认不启用。如果启用,则会把JSON或fastjson类型的返回值转换为Map类型;如果返回类型和本地接口中定义的不同,则会做POJO的转换 | - |
ConsumerContextFilter | 为消费者把一些上下文信息设置到当前线程的RpcContext对象中,包括invocation、localhost、remote host等 | 消费者 |
ContextFilter | 同上,但是为服务提供者服务 | 服务提供者 |
DeprecatedFilter | 如果调用的方法被标记位已弃用,那么DeprecatedFilter将记录一个错误消息 | 消费者 |
EchoFilter | 用于echo测试 | 服务提供者 |
ExceptionFilter | 用于统一的异常处理,防止出现序列化失败 | 服务提供者 |
GenericFilter | 用于服务提供者端,实现泛化调用,实现序列化的检查和处理 | 服务提供者 |
GenericImplFilter | 同上,但用于消费者端 | 消费者 |
TimeoutFilter | 如果某些服务调用超时,则自动记录告警日志 | 服务提供者 |
TokenFilter | 服务提供者下令发牌给消费者,通常用于防止消费者绕过注册中心直接调用服务提供者 | 服务提供者 |
TpsLimitFilter | 用于服务端的限流,注意与ExecuteLimitFilter | 消费者 |
FutureFilter | 在发起invoke或得到返回值、出现异常的时候触发回调事件 | 消费者 |
TraceFilter | Trace指令的使用 | 服务提供者 |
MonitorFilter | 监控并统计所有的接口的调用情况,如成功、失败、耗时。后续DubboMonitor会定时把该过滤器收集的数据发送到Dubbo-Monitor服务上 | 服务提供者+消费者 |
每个过滤器的使用方不一样,有的是服务提供者使用,有的是消费者使用。Dubbo是如何保证服务提供者不会使用消费者的过滤器的呢?答案就在@Activate注解上,该注解可以设置过滤器激活的条件和顺序,如@Activate(group = Constants.PROVIDER, order = -110000)
表示在服务提供端扩展点实现才有效,并且过滤器的顺序是 -110000.
过滤器初始化的实现原理
服务的暴露与引用会使用Protocol层,而ProtocolFilterWrapper包装类则实现了过滤器链的组装。在服务的暴露与引用过程中,会使用ProtocolFilterWrapper#buildInvokerChain
方法组装整个过滤器链:
buildInvokerChain方法构造调用链的步骤:
- 获取并遍历所有过滤器。通过
ExtensionLoader#getActivateExtension
方法获取所有的过滤器并遍历。 - 使用装饰器模式,增强原有Invoker,组装过滤器链。使用装饰器模式,像俄罗斯套娃一样,把过滤器一个又一个套到Invoker上。
为什么要倒排遍历呢?
因为是通过从里到外构造匿名类的方式构造Invoekr的,所以只有倒排,最外层的Invoker才能是第一个过滤器。