dubbo过滤器

什么是 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;
    }
}

Result接口源码,包含了rpc接口的执行结果与异常等信息

/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.rpc;

import java.util.Map;

/**
 * RPC invoke result. (API, Prototype, NonThreadSafe)
 * 
 * @serial Don't change the class name and package name.
 * @see com.alibaba.dubbo.rpc.Invoker#invoke(Invocation)
 * @see com.alibaba.dubbo.rpc.RpcResult
 * @author qianlei
 * @author william.liangf
 */
public interface Result {

	/**
	 * Get invoke result.
	 * 
	 * @return result. if no result return null.
	 */
	Object getValue();

	/**
	 * Get exception.
	 * 
	 * @return exception. if no exception return null.
	 */
	Throwable getException();

    /**
     * Has exception.
     * 
     * @return has exception.
     */
    boolean hasException();

    /**
     * Recreate.
     * 
     * <code>
     * if (hasException()) {
     *     throw getException();
     * } else {
     *     return getValue();
     * }
     * </code>
     * 
     * @return result.
     * @throws if has exception throw it.
     */
    Object recreate() throws Throwable;

    /**
     * @deprecated Replace to getValue()
     * @see com.alibaba.dubbo.rpc.Result#getValue()
     */
    @Deprecated
    Object getResult();


    /**
     * get attachments.
     *
     * @return attachments.
     */
    Map<String, String> getAttachments();

    /**
     * get attachment by key.
     *
     * @return attachment value.
     */
    String getAttachment(String key);

    /**
     * get attachment by key with default value.
     *
     * @return attachment value.
     */
    String getAttachment(String key, String defaultValue);

}

配置过滤器

在 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去指定(在filter接口的实现类上标注)
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;
}

用法

  1. 指定过滤器使用方
    group
    例子 - 指定生产者使用
    @Activate(group = {Constants.PROVIDER})

  2. 指定特定参数
    value
    例子 - 指定参数 accessToken=123的参数使用
    @Activate(“accessToken=123”)
    例子 - 指定有 accessToken参数使用
    @Activate(“accessToken”)

  3. 指定过滤器顺序
    order
    例子 - 数字越小优先级越高
    @Activate(order = -1)

猜你喜欢

转载自blog.csdn.net/m0_37556444/article/details/84623578