什么是 dubbo 过滤器
学过servlet 或 spring mvc 的同学都知道有一个叫作过滤器的东西。顾名思义他就是用于过滤的。它本身不产生请求或者响应,它只是修改对某一资源的请求,或者修改某一资源的响应。
为什么需要 dubbo 过滤器
对于有些操作,我们可能每个接口都需要使用。比如打印接口调用的请求与响应信息,计算接口调用的时间。或者字符编码的转换。再或者就接口的权限判断。。。不胜枚举。对于这些通用且是大多数接口都会使用的内容。如果在第一个接口里面去实现或者调用,就太过于麻烦。这个时候就可以把它抽到一个调用接口时都会经过的地方去。这个地方就是过滤器。其实可以简单理解成接口层面的 AOP。
什么时候运行
一般情况下过滤器不只一个。比如说有一个编码转换的过滤器,一个接口调用日志的过滤器,两个同时存在。因为打印日志时是使用一个固定编码的。所以为了防止打印时不产生乱码,必须要在编码转换过滤器之后运行。这种情况下我们就需要一个顺序。这也就形成了常说的过滤器链。至于如何控制顺序,会在下文介绍。
在哪种场景下使用
主要是一些通用的操作
接口的调用日志打印
接口权限判断
编码转换
相关角色有哪些
对于 servlet 来说,一个是接口的调用方比如浏览器,爬虫。。。。,一个是提供的接口。对于dubbo 来说,一个是生产者,一个是消费者。
怎么去使用 dubbo 过滤器
- 继承com.alibaba.dubbo.rpc.Filter并且实现它的 invoke 方法。
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;
}
}
配置过滤器
在 resources 目录下创建一个名为 META-INF 的目录
在 META-INF 的目录下创建一个名为 dubbo 目录
在 dubbo 目录下创建一个名为 com.alibaba.dubbo.rpc.Filter 的文本文件
在文本中配置自定义的过滤器
文件目录如下:
com.alibaba.dubbo.rpc.Filter文件内容如下:
内容: 过滤器名称=过滤器全限定名
logFilter=com.fshows.fsframework.extend.dubbo.filter.LogFilter
至于为什么要这些写?因为这里使用的是 SPI 机制。类似于 spring 的 IOC,详情可以看理解的Java中SPI机制
- 指定过滤器的拦截对象与过滤器顺序
通过@Adaptive去指定
- Adaptive 源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Group过滤条件。
* <br />
* 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
* <br />
* 如没有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})
指定特定参数
value
例子 - 指定参数 accessToken=123的参数使用
@Activate("accessToken=123")
例子 - 指定有 accessToken参数使用
@Activate("accessToken")
指定过滤器顺序
order
例子 - 数字越小优先级越高
@Activate(order = -1)