Dubbo-Filter机制概述

1.Filter介绍

Dubbo引入过滤器链机制来实现功能的包装(或扩展)。Dubbo很多功能,例如泛化调用、并发控制等都是基于Filter机制实现的,系统默认的Filter在/dubbo-rpc-api/src/main/resources/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter文件中定义,内容如下:

echo=com.alibaba.dubbo.rpc.filter.EchoFilter
generic=com.alibaba.dubbo.rpc.filter.GenericFilter
genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
token=com.alibaba.dubbo.rpc.filter.TokenFilter
accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
context=com.alibaba.dubbo.rpc.filter.ContextFilter
consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter

以其中一个来说明一下Filter的定义要素:

/**
 * EchoInvokerFilter
 */
@Activate(group = Constants.PROVIDER, order = -110000)    // @2
public class EchoFilter implements Filter {                                 // @1

    @Override
    public Result invoke(Invoker invoker, Invocation inv) throws RpcException {
        if (inv.getMethodName().equals(Constants.$ECHO) && inv.getArguments() != null && inv.getArguments().length == 1)
            return new RpcResult(inv.getArguments()[0]);
        return invoker.invoke(inv);
    }
}

代码@1:实现com.alibaba.dubbo.rpc.Filter接口。
代码@2:添加Activate,其注解含义如下:

  • group: 所属组,String[],例如消费端、服务端。
  • value String[],如果指定该值,只有当消费者或服务提供者URL中包含属性名为value的键值对,该过滤器才处于激活状态。
  • before:String[],用于指定执行顺序,before指定的过滤器在该过滤器之前执行。
  • after:string[],用于指定执行顺序,after指定的过滤器在该过滤器之后执行。
  • order:用户指定顺序,值越小,越先执行。

除了支持默认的过滤器外,Dubbo还支持自定义Filter,可以通过service.filter指定过滤器,多个用英文逗号隔开,其配置方法为:

 
     
 

当然,可以为所有服务提供者设置共用过滤器,其指定方法为:

 
    
 

消费端自定义过滤器的key为reference.filter,其使用方法在< dubbo:reference/>标签或< dubbo:consumer/>标签下定义属性。

2.创建分析

调用链的构建是通过下面的方法来实现:

com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper.buildInvokerChain(Invoker, String, String)

该方法源码如下:

 private static  Invoker buildInvokerChain(final Invoker invoker, String key, String group) {
        Invoker last = invoker;
        List filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i --) {
                final Filter filter = filters.get(i);
                final Invoker next = last;
                last = new Invoker() {

                    public Class getInterface() {
                        return invoker.getInterface();
                    }

                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
}

在构建调用链时方法先获取Filter列表,然后创建与Fitler数量一样多Invoker结点,接着将这些结点串联在一起,构成一个链表,最后将这个链的首结点返回,随后的调用中,将从首结点开始,依次调用各个结点,完成调用后沿调用链返回。这里各个Invoker结点的串联是通过与其关联的invoke方法来完成的。接下来分析这个调用链的创建。

buildInvokerChain(final Invoker invoker, String key, String group)

调用buildInvokerChain时会传入invoker参数:

Invoker last = invoker; 

通过创建last,来指向头结点。初始状态如下图所示:

接着通过循环遍历获取到的Filter,同时创建Invoker结点,每个结点对应一个Filter。此时循环内部定义了next指针。

final Invoker next = last;

该指针每次更新为指向原链表中的头结点,即last指针。 随后创建新结点,并更新last。此时新结点将作为链表中的头结点。

final Invoker next = last;
last = new Invoker(),

结果如下图所示。

Dubbo-Filter机制概述_第1张图片

接着通过invoke方法将各个结点串联。

public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
}

在该方法内部,通过调用与该invoker关联的filter中的invoke方法来实现结点的连接。调用时将next传入invoke方法,在调用时首先会调用该结点对应的filter的invoke方法,接着调用传入参数next的invoke方法。Next的invoke方法同样会调用自己所关联的filter的invoke方法,这样就完成了结点的串联。

Dubbo-Filter机制概述_第2张图片

可以看到链表的最后一个结点就是buildInvokerChain 方法的入参invoker。最终buildInvokerChain方法通过链表头插法完成调用链的创建。因此在真正的调用请求处理前会经过若干filter进行预处理。

这里调用链的创建可以看作是职责链模式(Chain of Responsibility Pattern)的一个实现。这样系统中增加一个新的过滤器预处理请求时,无须修改原有系统的代码,只需重新建调用链即可。

3.自定义Filter

以为dubbo接口增加IP白名单为例,在开发dubbo接口时,有时可能会限制接口的访问,ip白名单即是一种。在dubbo中,通过扩展Filter接口,可以实现IP白名单的功能。

先定义一个配置IP白名单的bean:

/**
 * Created by j.tommy on 2017/11/4.
 */
public class IPWhiteList {
    private boolean isEnabled; // 是否启用白名单
    private List allowIps; // 允许的白名单列表
    public boolean isEnabled() {
        return isEnabled;
    }
    public void setEnabled(boolean isEnabled) {
        this.isEnabled = isEnabled;
    }
    public List getAllowIps() {
        return allowIps;
    }
    public void setAllowIps(List allowIps) {
        this.allowIps = allowIps;
    }
}

然后我们实现dubbo的Filter接口:

/**
 * Created by j.tommy on 2017/11/4.
 */
public class IPWhiteListFilter implements Filter{
    private IPWhiteList ipWhiteList;
    public void setIpWhiteList(IPWhiteList ipWhiteList) {
        this.ipWhiteList = ipWhiteList;
    }
    @Override
    public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
        if (!ipWhiteList.isEnabled()) {
            System.out.println("dubbo IP白名单被禁用!");
            return invoker.invoke(invocation);
        }
        String clientIp = RpcContext.getContext().getRemoteHost();
        if (ipWhiteList.getAllowIps().contains(clientIp)) {
            return invoker.invoke(invocation);
        }
        System.out.println("dubbo客户端IP[" + clientIp + "]不在白名单,禁止调用!");
        return new RpcResult();
    }
}

在/resources目录下,新建META-INF/dubbo目录,并新建一个名为com.alibaba.dubbo.rpc.Filter的文本文件 ,内容如下:

ipWhiteListFilter=com.tommy.service.provider.filter.IPWhiteListFilter

dubbo的配置文件中增加filter的配置:



    
    
    
    
    
    
    
        
        
            
                127.0.0.1
                192.168.71.170
            
        
    

你可能感兴趣的:(java,java,后端)