• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    以其中一个来说明一下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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    代码@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指定过滤器,多个用英文逗号隔开,其配置方法为:

     
         
     
    
    • 1
    • 2
    • 3

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

     
        
     
    
    • 1
    • 2
    • 3

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

    2.创建分析

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

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

    该方法源码如下:

     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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

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

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

    调用buildInvokerChain时会传入invoker参数:

    Invoker last = invoker; 
    
    • 1

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

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

    final Invoker next = last;
    
    • 1

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

    final Invoker next = last;
    last = new Invoker(),
    
    • 1
    • 2

    结果如下图所示。

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

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

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

    可以看到链表的最后一个结点就是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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    然后我们实现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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

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

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

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

    
    
        
        
        
        
        
        
        
            
            
                
                    127.0.0.1
                    192.168.71.170
                
            
        
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    一文讲懂GeoHash技术(一)
    大数据课程K22——Spark的SparkSQL的API调用
    334 - 线程通信问题
    一文认识蓝牙(验证基于Aduino IDE的ESP32)
    DFS-4-N皇后问题
    【版本2020.03】使用idea导入maven项目
    李沐:用随机梯度下降来优化人生!
    如何在 iPhone 上检索已删除的短信
    鸿蒙入门12-Divider
    探索Docker:原理、安装与基础应用
  • 原文地址:https://blog.csdn.net/m0_67402970/article/details/126327867