Filter 和 servlet 中的过滤器类似,可以为RPC请求增加一层切面(不是基于AOP),进行统一的参数校验、接口耗时统计等操作。
有时也叫拦截器。
使用要点:
- 若要使 filter 生效,必须在
META-INF/dubbo/org.apache.dubbo.rpc.Filter
声明 Filter,可以在当前项目中,也可以在一个单独的jar中。dubbo 会尝试获取 classpath 中的所有META-INF/dubbo/org.apache.dubbo.rpc.Filter
文件。 - 若要使 filter 生效,下面两个必须要满足至少其中一个:
- Filter 使用了
@Activate
注解。 - 按照下面的方法在xml配置文件中进行配置:
- Filter 使用了
<!-- 消费方调用过程拦截 -->
<dubbo:reference filter="xxx,yyy" />
<!-- 消费方调用过程缺省拦截器,将拦截所有reference -->
<dubbo:consumer filter="xxx,yyy"/>
<!-- 提供方调用过程拦截 -->
<dubbo:service filter="xxx,yyy" />
<!-- 提供方调用过程缺省拦截器,将拦截所有service -->
<dubbo:provider filter="xxx,yyy"/>
- 使用 telnet 进行接口测试时,过滤器
不生效
。 - RPC 调用和泛化调用,过滤器都会生效。
- @Activate 注解中的 order 值越小,优先级越高,越早执行,越晚结束。
默认的过滤器
在 dubbo 源码中,META-INF/dubbo/internal/org.apache.dubbo.rpc.Filter
配置了默认的过滤器:
cache=org.apache.dubbo.cache.filter.CacheFilter
validation=org.apache.dubbo.validation.filter.ValidationFilter
echo=org.apache.dubbo.rpc.filter.EchoFilter
generic=org.apache.dubbo.rpc.filter.GenericFilter
genericimpl=org.apache.dubbo.rpc.filter.GenericImplFilter
token=org.apache.dubbo.rpc.filter.TokenFilter
accesslog=org.apache.dubbo.rpc.filter.AccessLogFilter
activelimit=org.apache.dubbo.rpc.filter.ActiveLimitFilter
classloader=org.apache.dubbo.rpc.filter.ClassLoaderFilter
context=org.apache.dubbo.rpc.filter.ContextFilter
consumercontext=org.apache.dubbo.rpc.filter.ConsumerContextFilter
exception=org.apache.dubbo.rpc.filter.ExceptionFilter
executelimit=org.apache.dubbo.rpc.filter.ExecuteLimitFilter
deprecated=org.apache.dubbo.rpc.filter.DeprecatedFilter
compatible=org.apache.dubbo.rpc.filter.CompatibleFilter
timeout=org.apache.dubbo.rpc.filter.TimeoutFilter
trace=org.apache.dubbo.rpc.protocol.dubbo.filter.TraceFilter
future=org.apache.dubbo.rpc.protocol.dubbo.filter.FutureFilter
monitor=org.apache.dubbo.monitor.support.MonitorFilter
metrics=org.apache.dubbo.monitor.support.MetricsFilter
有一些过滤器使用了@Activate
注解,所以默认会生效。
有些过滤器会在 consumer 端生效,有些是在 provider 生效,有些是两端都生效,有些还需要其他的一些条件才生效。
这些过滤器统称为default
。
示例1:入门示例
项目结构:
├── build.gradle
└── src
└── main
├── java
│ ├── consumer // 消费端
│ │ ├── ConsumerMain.java // PRC 调用
│ │ └── GenericConsumerMain.java // 泛化调用
│ ├── contract // 协议
│ │ └── ISayHiRpcService.java
│ ├── filter // 过滤器
│ │ ├── CustomFilter01.java
│ │ └── CustomFilter02.java
│ └── provider // 服务端
│ ├── ProviderMain.java // 服务端启动类
│ └── rpc
│ └── SayHiRpcServiceImpl.java
└── resources
├── META-INF
│ └── dubbo
│ └── org.apache.dubbo.rpc.Filter // 声明过滤器
├── dubbo-consumer.xml // 消费端配置
├── dubbo-provider.xml // 服务端配置
├── dubbo.properties
└── log4j.properties
Filter 实现
自定义过滤器 CustomFilter01.java 内容如下:
package filter;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
@Activate(group = {Constants.PROVIDER}) // 服务端生效
public class CustomFilter01 implements org.apache.dubbo.rpc.Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
System.out.println("CustomFilter01 开始");
Result result = invoker.invoke(invocation);
System.out.println("CustomFilter01 结束");
return result;
}
}
自定义过滤器 CustomFilter02.java 内容如下:
package filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;
public class CustomFilter02 implements org.apache.dubbo.rpc.Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
System.out.println("CustomFilter02 开始");
Result result = invoker.invoke(invocation);
System.out.println("CustomFilter02 结束");
return result;
}
}
META-INF/dubbo/org.apache.dubbo.rpc.Filter
文件中内容如下:
customFilter01=filter.CustomFilter01
RPC 协议
contract.ISayHiRpcService 内容:
package contract;
public interface ISayHiRpcService {
String sayHi();
}
provider 实现
dubbo-provider.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="provider-demo"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="20881"/>
<dubbo:service interface="contract.ISayHiRpcService" class="provider.rpc.SayHiRpcServiceImpl"/>
</beans>
ISayHiRpcService 接口协议实现:
package provider.rpc;
import contract.ISayHiRpcService;
import org.springframework.stereotype.Service;
@Service
public class SayHiRpcServiceImpl implements ISayHiRpcService {
@Override
public String sayHi() {
return "Hi";
}
}
consumer 实现
dubbo-consumer.xml 内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="consumer-demo"/>
<dubbo:consumer timeout="5000" />
<dubbo:registry address="zookeeper://127.0.0.1:2181" check="false"/>
<dubbo:reference id="sayHiRpcService"
interface="contract.ISayHiRpcService"
/>
</beans>
消费者 ConsumerMain 实现:
package consumer;
import contract.ISayHiRpcService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ImportResource({"dubbo-consumer.xml"})
public class ConsumerMain {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConsumerMain.class);
ISayHiRpcService demoService = ctx.getBean(ISayHiRpcService.class);
String s = demoService.sayHi();
System.out.println("rpc返回结果: " + s);
}
}
测试
- 启动 zookeeper
- 启动 PoviderMain 类
- 启动 ConsumerMain 类
ConsumerMain 的执行结果:
rpc返回结果: Hi
ProviderMain 的日志:
CustomFilter01 开始
CustomFilter01 结束
可以看到 CustomFilter01 生效了。
示例2: 在配置文件中指定filter
在示例1中,CustomFilter01 声明了@Activate
注解。
@Activate(group = {Constants.PROVIDER}) // 服务端生效
public class CustomFilter01 implements org.apache.dubbo.rpc.Filter {
将其去掉,
- 重新启动 PoviderMain 类
- 重新启动 ConsumerMain 类
可以看到 PoviderMain 中 CustomFilter01 的日志没有打印出来,也就是 CustomFilter01 未生效。
在 provider 配置文件 dubbo-provider.xml 中修改配置,指定 filter :
<dubbo:service filter="customFilter01" interface="contract.ISayHiRpcService" class="provider.rpc.SayHiRpcServiceImpl"/>
重新测试,PoviderMain 打印出了 CustomFilter01 的日志:
CustomFilter01 开始
CustomFilter01 结束
也可以同时在 consumer 配置文件 dubbo-consumer.xml 中配置消费者的 filter:
<dubbo:reference filter="customFilter01" id="sayHiRpcService" interface="contract.ISayHiRpcService"/>
运行 ConumserMain ,会输出:
CustomFilter01 开始
CustomFilter01 结束
rpc返回结果: Hi
示例3:去掉默认的过滤器
以 provider 为例,在 filter 属性中增加-default
即可。
<dubbo:service filter="-default,customFilter01" interface="contract.ISayHiRpcService" class="provider.rpc.SayHiRpcServiceImpl"/>
可以使用断点调试法验证。
示例4:配置文件中 filter 的相对顺序决定了执行的相对顺序
在META-INF/dubbo/org.apache.dubbo.rpc.Filter
中声明两个 Filter:
customFilter01=filter.CustomFilter01
customFilter02=filter.CustomFilter02
dubbo-provider.xml 中配置filter如下:
<dubbo:service filter="customFilter01,customFilter02" interface="contract.ISayHiRpcService" class="provider.rpc.SayHiRpcServiceImpl"/>
- 重新启动 PoviderMain 类
- 重新启动 ConsumerMain 类
PoviderMain 可以看到日志:
CustomFilter01 开始
CustomFilter02 开始
CustomFilter02 结束
CustomFilter01 结束
调整 filter 顺序:
<dubbo:service filter="customFilter02,customFilter01" interface="contract.ISayHiRpcService" class="provider.rpc.SayHiRpcServiceImpl"/>
再次测试,PoviderMain 可以看到日志:
CustomFilter02 开始
CustomFilter01 开始
CustomFilter01 结束
CustomFilter02 结束
示例5:使用 @Activate 配置 filter 执行顺序
@Activate 注解中的 order 值越小,优先级越高,越早执行,越晚结束。
CustomFilter01 增加 @Activate 注解,并指定 order。
@Activate(group = {Constants.PROVIDER}, order = 2)
public class CustomFilter01 implements org.apache.dubbo.rpc.Filter {
CustomFilter02 增加 @Activate 注解,并指定 order。
@Activate(group = {Constants.PROVIDER}, order = 1)
public class CustomFilter02 implements org.apache.dubbo.rpc.Filter {
provider 配置文件 dubbo-provider.xml 中不声明 filter 属性:
<dubbo:service interface="contract.ISayHiRpcService" class="provider.rpc.SayHiRpcServiceImpl"/>
再次测试,PoviderMain 可以看到日志:
CustomFilter02 开始
CustomFilter01 开始
CustomFilter01 结束
CustomFilter02 结束
调换 CustomFilter01、CustomFilter02 的 order 值,
@Activate(group = {Constants.PROVIDER}, order = 1)
public class CustomFilter01 implements org.apache.dubbo.rpc.Filter {
@Activate(group = {Constants.PROVIDER}, order = 2)
public class CustomFilter02 implements org.apache.dubbo.rpc.Filter {
ProviderMain 测试结果:
CustomFilter01 开始
CustomFilter02 开始
CustomFilter02 结束
CustomFilter01 结束
问答
<dubbo:service filter="xxx,yyy" /> 未声明默认过滤器时,默认过滤器生效吗?
有一些默认过滤器使用了@Activate
注解,所以默认会生效。
@Activate
注解声明了服务端生效,这种过滤器可以给消费端用吗?
可以。在 xml 中配置即可。
@Activate 配置了order, xml 配置文件中配置了filter,执行顺序以哪个为准?
以配置文件为准。