dubbo 过滤器使用 - 图1

什么是 dubbo 过滤器

学过servlet 或 spring mvc 的同学都知道有一个叫作过滤器的东西。顾名思义他就是用于过滤的。它本身不产生请求或者响应,它只是修改对某一资源的请求,或者修改某一资源的响应。

为什么需要 dubbo 过滤器

对于有些操作,我们可能每个接口都需要使用。比如打印接口调用的请求与响应信息,计算接口调用的时间。或者字符编码的转换。再或者就接口的权限判断。。。不胜枚举。对于这些通用且是大多数接口都会使用的内容。如果在第一个接口里面去实现或者调用,就太过于麻烦。这个时候就可以把它抽到一个调用接口时都会经过的地方去。这个地方就是过滤器。其实可以简单理解成接口层面的 AOP。

什么时候运行

一般情况下过滤器不只一个。比如说有一个编码转换的过滤器,一个接口调用日志的过滤器,两个同时存在。因为打印日志时是使用一个固定编码的。所以为了防止打印时不产生乱码,必须要在编码转换过滤器之后运行。这种情况下我们就需要一个顺序。这也就形成了常说的过滤器链。至于如何控制顺序,会在下文介绍。

在哪种场景下使用

主要是一些通用的操作

  1. 接口的调用日志打印

  2. 接口权限判断

  3. 编码转换

相关角色有哪些

对于 servlet 来说,一个是接口的调用方比如浏览器,爬虫。。。。,一个是提供的接口。对于dubbo 来说,一个是生产者,一个是消费者。

怎么去使用 dubbo 过滤器

  1. 继承com.alibaba.dubbo.rpc.Filter并且实现它的 invoke 方法。
  1. package com.fshows.fsframework.extend.dubbo.filter;
  2. import com.alibaba.dubbo.rpc.Filter;
  3. import com.alibaba.dubbo.rpc.Invocation;
  4. import com.alibaba.dubbo.rpc.Invoker;
  5. import com.alibaba.dubbo.rpc.Result;
  6. import com.alibaba.dubbo.rpc.RpcException;
  7. import com.fshows.fsframework.core.utils.SystemClock;
  8. import lombok.extern.slf4j.Slf4j;
  9. /**
  10. * @author buhao
  11. * @version LogFilter.java, v 0.1 2018-09-18 20:40 buhao
  12. */
  13. @Slf4j
  14. public class LogFilter implements Filter {
  15. @Override
  16. public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
  17. // 获得 RPC 方法名
  18. String methodName = invoker.getUrl().getPath();
  19. // 获得开始时间
  20. long startTime = SystemClock.millisClock().now();
  21. // 打印调用前日志
  22. // 获得参数
  23. Object[] arguments = invocation.getArguments();
  24. log.info("RPC 接口开始 methodName = {}, agruments = {}", methodName, arguments);
  25. // 调用接口
  26. Result result = invoker.invoke(invocation);
  27. // 打印调用后日志
  28. // 抛出的异常
  29. Throwable exception = result.getException();
  30. // 返回结果
  31. Object value = result.getValue();
  32. // 打印结束日志
  33. if (exception != null) {
  34. log.info("RPC 接口异常结束 methodName = {}, exception = {}, time = {}ms ", methodName, exception, SystemClock.millisClock().now() - startTime);
  35. } else {
  36. log.info("RPC 接口结束 methodName = {}, result ={}, time = {}ms ", methodName, value, SystemClock.millisClock().now() - startTime);
  37. }
  38. return result;
  39. }
  40. }
  1. 配置过滤器

    1. 在 resources 目录下创建一个名为 META-INF 的目录

    2. 在 META-INF 的目录下创建一个名为 dubbo 目录

    3. 在 dubbo 目录下创建一个名为 com.alibaba.dubbo.rpc.Filter 的文本文件

    4. 在文本中配置自定义的过滤器

文件目录如下:

dubbo 过滤器使用 - 图2

com.alibaba.dubbo.rpc.Filter文件内容如下:

内容: 过滤器名称=过滤器全限定名

  1. logFilter=com.fshows.fsframework.extend.dubbo.filter.LogFilter

至于为什么要这些写?因为这里使用的是 SPI 机制。类似于 spring 的 IOC,详情可以看理解的Java中SPI机制

  1. 指定过滤器的拦截对象与过滤器顺序

通过@Adaptive去指定

  • Adaptive 源码
  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target({ElementType.TYPE, ElementType.METHOD})
  4. public @interface Activate {
  5. /**
  6. * Group过滤条件。
  7. * <br />
  8. * 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
  9. * <br />
  10. * 如没有Group设置,则不过滤。
  11. */
  12. String[] group() default {};
  13. /**
  14. * Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。
  15. * <p/>
  16. * 示例:<br/>
  17. * 注解的值 <code>@Activate("cache,validatioin")</code>,
  18. * 则{@link ExtensionLoader#getActivateExtension}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展。
  19. * <br/>
  20. * 如没有设置,则不过滤。
  21. */
  22. String[] value() default {};
  23. /**
  24. * 排序信息,可以不提供。
  25. */
  26. String[] before() default {};
  27. /**
  28. * 排序信息,可以不提供。
  29. */
  30. String[] after() default {};
  31. /**
  32. * 排序信息,可以不提供。
  33. */
  34. int order() default 0;
  35. }
  • 用法

    • 指定过滤器使用方

      • group

        • 例子 - 指定生产者使用

            1. @Activate(group = {Constants.PROVIDER})
    • 指定特定参数

      • value

        • 例子 - 指定参数 accessToken=123的参数使用

            1. @Activate("accessToken=123")
        • 例子 - 指定有 accessToken参数使用

            1. @Activate("accessToken")
    • 指定过滤器顺序

      • order

        • 例子 - 数字越小优先级越高

            1. @Activate(order = -1)

参考

Dubbo过滤器Filter的使用及应用场景
理解的Java中SPI机制
dubbo官方文档-扩展点加载