Dubbo:过滤器 Filter


#Dubbo#


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配置文件中进行配置:
<!-- 消费方调用过程拦截 -->
<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);
    }
}

测试

  1. 启动 zookeeper
  2. 启动 PoviderMain 类
  3. 启动 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 {

将其去掉,

  1. 重新启动 PoviderMain 类
  2. 重新启动 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"/>
  1. 重新启动 PoviderMain 类
  2. 重新启动 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,执行顺序以哪个为准?

以配置文件为准。


( 本文完 )