什么是 dubbo 过滤器

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

为什么需要 dubbo 过滤器

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

什么时候运行

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

在哪种场景下使用

主要是一些通用的操作

  1. 接口的调用日志打印

  2. 接口权限判断

  3. 编码转换

相关角色有哪些

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

怎么去使用 dubbo 过滤器

  1. 继承com.alibaba.dubbo.rpc.Filter并且实现它的 invoke 方法。
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
package com.fshows.fsframework.extend.dubbo.filter;

import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcException;
import com.fshows.fsframework.core.utils.SystemClock;
import lombok.extern.slf4j.Slf4j;

/**
* @author buhao
* @version LogFilter.java, v 0.1 2018-09-18 20:40 buhao
*/
@Slf4j
public class LogFilter implements Filter {

@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// 获得 RPC 方法名
String methodName = invoker.getUrl().getPath();
// 获得开始时间
long startTime = SystemClock.millisClock().now();

// 打印调用前日志
// 获得参数
Object[] arguments = invocation.getArguments();

log.info("RPC 接口开始 methodName = {}, agruments = {}", methodName, arguments);


// 调用接口
Result result = invoker.invoke(invocation);

// 打印调用后日志
// 抛出的异常
Throwable exception = result.getException();
// 返回结果
Object value = result.getValue();
// 打印结束日志
if (exception != null) {
log.info("RPC 接口异常结束 methodName = {}, exception = {}, time = {}ms ", methodName, exception, SystemClock.millisClock().now() - startTime);
} else {
log.info("RPC 接口结束 methodName = {}, result ={}, time = {}ms ", methodName, value, SystemClock.millisClock().now() - startTime);
}

return result;
}
}
  1. 配置过滤器

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

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

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

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

文件目录如下:

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

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

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

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

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

通过@Adaptive 去指定

  • Adaptive 源码
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
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Group过滤条件。
*

* 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
*

* 如没有Group设置,则不过滤。
*/
String[] group() default {};

/**
* Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。
* <p/>
* 示例:<br/>
* 注解的值 <code>@Activate("cache,validatioin")</code>,
* 则{@link ExtensionLoader#getActivateExtension}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展。
* <br/>
* 如没有设置,则不过滤。
*/
String[] value() default {};

/**
* 排序信息,可以不提供。
*/
String[] before() default {};

/**
* 排序信息,可以不提供。
*/
String[] after() default {};

/**
* 排序信息,可以不提供。
*/
int order() default 0;
}
  • 用法

    • 指定过滤器使用方

      - group
      
         - 例子 - 指定生产者使用
      
            - ```

      @Activate(group = {Constants.PROVIDER})

1
2
3
4
5
6
7
8
9

- 指定特定参数

- value

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

- ```java
@Activate("accessToken=123")
     - 例子 - 指定有 accessToken参数使用

        - ```java

@Activate(“accessToken”)

1
2
3
4
5
6
7
8
9

- 指定过滤器顺序

- order

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

- ```java
@Activate(order = -1)

参考

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

本文地址 https://blog.coder4j.cn/posts/e57e2db9/