dubbo filter源码分析

1,286 阅读4分钟

filter概述

  • dubbo filter与servlet filter 或 XXX filter ... 设计思路一样;在目标方法执行前, 后插入filter功能代码,实现类似AOP的能力
  • 多个filter在排好序后组成调用链执行;责任链设计模式
  • dubbo filter 通常分为 :provider 和 consumer两种,通过 @Activate(group = CONSUMER) 注解中group参数可进行区分,服务调用方 只会加载执行“group = CONSUMER”的filter,服务提供方“group = Provider”的filter

dubbo filter源码分析

相关接口概念

  • Invocation,Invoker,Invoker 之间仅存在方法参数层面的依赖关系

  • Invocation 可理解为java方法的元数据对象

  • Invoker 可理解为java方法执行调用器(Invocation 作为方法参数)

  • Invoker 执行invoke方法可理解为目标方法被执行

  • Filter 中接口方法持有Invoker,Invocation,可理解为目标方法执行器与目标方法元数据

    @SPI
    public interface Filter {
        /**
         * filter 接口方法
         * invoker 方法调用对象
         * invocation 方法名/参数/返回值... 数据结构
         */
        Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
    
        interface Listener {
            // 调用调用
            void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation);
            // 异常调用
            void onError(Throwable t, Invoker<?> invoker, Invocation invocation);
        }
    }
    
    // 方法调用对象 
    public interface Invoker<T> extends Node {
        Class<T> getInterface();
        // 方法调用
        Result invoke(Invocation invocation) throws RpcException;
    }
    
    // 方法对象
    public interface Invocation {
        String getTargetServiceUniqueName();
        String getMethodName();
        String getServiceName();
        ...
    }
    

filter的链式调用

ProtocolFilterWrapper#buildInvokerChain方法

  • ExtensionLoader加载出本次调用需要的filter列表,并已排好序

  • buildInvokerChain 方法参数中的invoker 可理解为最初的目标方法执行器

  • for循环为每个filter创建一个invoker并在invoker#invoke 方法中调用filter#invoke方法;filter0对应invoker0... filter2对应invoker2,srcInvoker为ProtocolFilterWrapper#buildInvokerChain中Invoker参数对象

  • 具体的每个filter实现类会调用 invoker.invoke(invocation);

  • for循环倒序创建invoker,循环结束形成的invoker链的执行顺序与filter列表顺序一致

  • 一次rpc调用  filter 与 invoker 对象有n 个与n+1个,但Invocation对象只有一个;【姑且解释为invoker 与 Invocation分开设计的一个原因】

  • 初次接触filter,invoker链可能有点混乱,建议看下图或自己画图

  • 调用关系如图:

    public class ProtocolFilterWrapper implements Protocol {
    
        // invoker 为目标方法
        private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
            // 目标方法
            Invoker<T> last = invoker;
            // 加载active中的filter 已根据order排好序
            List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
            if (!filters.isEmpty()) {
                // for循环 倒序将filter包装成invoker对象
                for (int i = filters.size() - 1; i >= 0; i--) {
                    final Filter filter = filters.get(i);
                    final Invoker<T> next = last;
    
                    last = new Invoker<T>() {
                         public Result invoke(Invocation invocation) throws RpcException {
                             Result asyncResult;
                            try {
                                // 执行filter调用方法
                                asyncResult = filter.invoke(next, invocation);
                            } catch(Exception e) {
                                if (filter instanceof Filter.Listener) {
                                    Filter.Listener listener = (Filter.Listener) filter;
                                    listener.onError(e, invoker, invocation);
                                }
                            } final() {
    
                            }
    
                            asyncResult.whenCompleteWithContext(r, t) -> {
                                //  有实现Filter.Listener接口的filter实现类会执行此代码
                            	if (t == null && filter instanceof Filter.Listener) {
                            		listener.onResponse(r, invoker, invocation);
                            	}
                            	if (t == null && filter instanceof Filter.Listener) {
                            		listener.onError(t, invoker, invocation);
                            	}
                            }
                         }                     
                    }
                }
            }
            return last;
        }
    
    }
    

consumer, provider 分组

  • ProtocolFilterWrapper#export 服务提供方暴露方法时传入group="provider"参数

  • ProtocolFilterWrapper#refer 服务调用方引用方法时传入group="consumer"参数

  • export, refer 返回值是已包含filter链的invoker对象

    public class ProtocolFilterWrapper implements Protocol {
    
        @Override
        public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            if (UrlUtils.isRegistry(invoker.getUrl())) {
                return protocol.export(invoker);
            }
            return protocol.export(buildInvokerChain(invoker, "service.filter","provider"));
        }
    
        @Override
        public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
            if (UrlUtils.isRegistry(url)) {
                return protocol.refer(type, url);
            }
            return buildInvokerChain(protocol.refer(type, url), "reference.filter", "consumer");
        }
    
    }
    

dubbo 提供的filter

  • dubbo提供了很多filter有 访问|超时|异常 等日志打印类型, attr|context 等 参数设置类型,限流|异常封装|鉴权 等通用处理类型filter

  • ActiveLimitFilter 调用方并发数量控制,采用wati, notify 方式控制调用并发数

    @Activate(group = CONSUMER, value = ACTIVES_KEY)
    public class ActiveLimitFilter implements Filter, Filter.Listener {
    
        private static final String ACTIVELIMIT_FILTER_START_TIME = "activelimit_filter_start_time";
    
        @Override
        public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
            URL url = invoker.getUrl();
            String methodName = invocation.getMethodName();
            int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
            final RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());
            // 方法维度控制是否可调用【原子计数器判断,调用成功则并发数+1】
            // beginCount 返回false则调用并发数已达到最大值
            if (!RpcStatus.beginCount(url, methodName, max)) {
                long timeout = invoker.getUrl().getMethodParameter(invocation.getMethodName(), TIMEOUT_KEY, 0);
                long start = System.currentTimeMillis();
                long remain = timeout;
                synchronized (rpcStatus) {
                    // dubbo check 判断并发数是否可以自增
                    // 此处必须用while循环,因为每次执行结束会进行 notifyAll所有线程
                    while (!RpcStatus.beginCount(url, methodName, max)) {
                        try {
                            // 释放锁,并进入wait状态 && 最大等待时间可控
                            rpcStatus.wait(remain);
                        } catch (InterruptedException e) {
                            // ignore
                        }
                        long elapsed = System.currentTimeMillis() - start;
                        remain = timeout - elapsed;
                        if (remain <= 0) {
                            throw new RpcException(RpcException.LIMIT_EXCEEDED_EXCEPTION,...);
                        }
                    }
                }
            }
    
            invocation.put(ACTIVELIMIT_FILTER_START_TIME, System.currentTimeMillis());
    
            return invoker.invoke(invocation);
        }
    
         @Override
        public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {
            String methodName = invocation.getMethodName();
            URL url = invoker.getUrl();
            int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
            // notify 所有等待rpcStatus 锁线程
            RpcStatus.endCount(url, methodName, getElapsed(invocation), true);
            notifyFinish(RpcStatus.getStatus(url, methodName), max);
        }
    
        @Override
        public void onError(Throwable t, Invoker<?> invoker, Invocation invocation) {
            String methodName = invocation.getMethodName();
            URL url = invoker.getUrl();
            int max = invoker.getUrl().getMethodParameter(methodName, ACTIVES_KEY, 0);
    
            if (t instanceof RpcException) {
                RpcException rpcException = (RpcException) t;
                if (rpcException.isLimitExceed()) {
                    return;
                }
            }
            // 调用失败需要 调用并发数-1
            RpcStatus.endCount(url, methodName, getElapsed(invocation), false);
            // notify 所有等待rpcStatus 锁线程
            notifyFinish(RpcStatus.getStatus(url, methodName), max);
        }
    
        private void notifyFinish(final RpcStatus rpcStatus, int max) {
            if (max > 0) {
                synchronized (rpcStatus) {
                    rpcStatus.notifyAll();
                }
            }
        }
    
    }
    
  • ExecuteLimitFilter 服务端并发数控制,当超过最大设置并发数时则直接失败

  • TpsLimitFilter 服务器TPS限流filter,控制任意时间窗口内总的请求数量

_文中代码基于dubbo v_2.7.8-SNAPSHOT 版本,且有改动(为方便展示)