Dubbo中的过滤器和Web应用中的过滤器的概念是一样的,提供了在服务调用前后插入自定义逻辑的途径。过滤器是整个Dubbo框架中非常重要的组成部分,Dubbo中很多功能都是基于过滤器扩展而来的。过滤器提供了服务提供者和消费者调用过程的拦截,即每次都执行RPC调用的时候,对应的过滤器都会生效。虽然过滤器的功能强大,但由于每次调用时都会执行,因此在使用的时候需要注意它对性能的影响。
配置上的“潜规则”:
过滤器顺序:
用户自定义的过滤器的顺序默认会在框架内置过滤器之后,可以使用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或得到返回值、出现异常的时候触发回调事件消费者TraceFilterTrace指令的使用服务提供者MonitorFilter监控并统计所有的接口的调用情况,如成功、失败、耗时。后续DubboMonitor会定时把该过滤器收集的数据发送到Dubbo-Monitor服务上服务提供者+消费者每个过滤器的使用方不一样,有的是服务提供者使用,有的是消费者使用。Dubbo是如何保证服务提供者不会使用消费者的过滤器的呢?答案就在@Activate注解上,该注解可以设置过滤器激活的条件和顺序,如@Activate(group = Constants.PROVIDER, order = -110000)表示在服务提供端扩展点实现才有效,并且过滤器的顺序是 -110000.
服务的暴露与引用会使用Protocol层,而ProtocolFilterWrapper包装类则实现了过滤器链的组装。在服务的暴露与引用过程中,会使用ProtocolFilterWrapper#buildInvokerChain方法组装整个过滤器链:
buildInvokerChain方法构造调用链的步骤:
获取并遍历所有过滤器。通过ExtensionLoader#getActivateExtension方法获取所有的过滤器并遍历。使用装饰器模式,增强原有Invoker,组装过滤器链。使用装饰器模式,像俄罗斯套娃一样,把过滤器一个又一个套到Invoker上。为什么要倒排遍历呢?
因为是通过从里到外构造匿名类的方式构造Invoekr的,所以只有倒排,最外层的Invoker才能是第一个过滤器。