dubbo的filter

在聊Dubbo Filter之前,我们先说说Web标准的javax.servlet.Filter

public interface Filter {
  	// 初始化,Servlet容器启动时调用
    void init(FilterConfig var1) throws ServletException;

  	// 做过滤动作
    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
		
  	// 销毁,优雅停机才会调用
    void destroy();
}

以上是javax.servlet.Filter的接口定义。3个行为,分别是初始化,执行,销毁。

初始化和销毁动作,就不在这边聊了。看看doFilter行为

@Override
public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain filterChain) throws IOException, ServletException {
    // do business before
    filterChain.doFilter(httpRequest, response);
    // do business after
    return;
	}
	super.doFilter(httpRequest, response, filterChain);
}

实现了javax.servlet.Filter接口的实现类,需要实现doFilter方法。一般都会像上面这个示例一样编写代码。关键代码filterChain.doFilter(httpRequest, response);

这行代码需要开发者自己写,实际上是使用filterChain去调用下一个filterFilterChain 的实例中维护了一个过滤器链的列表。每次调用doFilter方法,就会去找下一个执行。直到最后,都执行完了就去执行servlet。看下Tomcat的源码实现。

// 过滤器链数组
private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];

private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) { // pos就是指当前要执行哪个
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
            filter = filterConfig.getFilter();
            // --------------------此处省略一堆代码-----------------------
            filter.doFilter(request, response, this);
            // --------------------此处省略一堆代码-----------------------
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
    		// 这边开始执行serlvet
    }

从这块代码可以看到Tomcat执行过滤器链的过程。有兴趣的朋友可以追踪下源码。把javax.servlet.Filter放在前面说,是因为Dubbo Filterjavax.servlet.Filter很像,至少对开发者来讲是这样的。

不同之处:Dubbo Filter过滤器链的封装方式和执行方式不太一样。javax.servlet.Filter可以很明显在ApplicationFilterChain中知道filters这个数组,很明显,它维护了一个过滤器链。而Dubbo Filter是使用了Wapper的方式。将所有的过滤器一个包着一个。看下面这张图,可能会有些感觉了。

dubbo的filter_第1张图片

图中:Filter1包装了Filter2Filter2包装了Filter3Filter3包装了真实的Invoker。关键代码在ProtocolFilterWrapper中。详细说它之前,请先了解下Dubbo扩展类的初始化。

看下Dubbo Wapper机制。包装类型。

private T createExtension(String name) {
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        injectExtension(instance);
      
      	// -----------------------重点关注-------------------------
      	// 循环当前这个扩展类型下的所有包装类型逐个包装并且初始化
        Set<Class<?>> wrapperClasses = cachedWrapperClasses;
        if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
            for (Class<?> wrapperClass : wrapperClasses) {
                instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
            }
        }
      	// -----------------------重点关注-------------------------
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                type + ")  could not be instantiated: " + t.getMessage(), t);
    }
}

上面注释中标注重点关注的代码,循环当前这个扩展类型下的所有包装类型逐个包装并且初始化。

比如:Protocol类型的扩展,有3个包装类,分别是ProtocolFilterWrapperProtocolListenerWrapperQosProtocolWrapper

filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapper

RegistryProtocolDubboProtocol在初始化的时候,就会被这3个包装类逐个包装。

看下Protocol模块的类图

dubbo的filter_第2张图片

上图类有点多,不过结果很清晰。看下Protocol的5个子类,分别是

AbstractProtocol(具体协议实现类都在这个分支上)

ProtocolFilterWrapperProtocolListenerWrapperQosProtocolWrapper(三个包装类)

RegistryProtocol(这个比较特殊,它算是协议模块的适配器,dubbo服务导出和引入里面有详细说明)

仔细分析结构之后会发现,Protocol模块并不复杂。

从上述代码可以看到,任何一个Protocol的子类(除包装类本身)在实例化的时候,都会被3个包装类包装一下,其中一个就是ProtocolFilterWrapper。那么当运行时在执行Protocol.exportProtocol.refer时,就会先执行ProtocolFilterWrapper.exportProtocolFilterWrapper.refer

什么时候会执行Protocol.exportProtocol.refer?这个问题请查看文档dubbo服务导出和引用,这边不做太多赘述。

重点来了,看下ProtocolFilterWrapper的实现。不得不把整个类贴出来了。我详细注释各个重点。

public class ProtocolFilterWrapper implements Protocol {

  	// 被包装的协议对象
    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }

  	// 用过滤器链包装
    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
      	// 从配置中获取需要所有的过滤器
      	// filter是有序的,按照开发者的配置顺序来
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
      	// 如果没有过滤器,啥也不干。
        if (!filters.isEmpty()) {
      			// 开始做循环,从最后一个开始获取,因为要把后执行的包装在内部,先执行的包装在最外面。
          	// 不理解可以再看看上面那张图
            for (int i = filters.size() - 1; i >= 0; i--) {
                final Filter filter = filters.get(i);
              	// 这个next的赋值,不知道怎么用语言表达,自己理解下。结合Invoker last = invoker;
                final Invoker<T> next = last;
                last = new Invoker<T>() {// 每次new一个新的Invoker实例

                    @Override
                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

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

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

                  	// 我们在filter的实现中会写这么一行代码Result result = invoker.invoke(invocation);
                  	// 这边的invoker对象,就是这边框架中的next对象。
                  	// 发现了没有,每次开发者的那行代码被执行时,就相当于执行了next.invoke(invocation);
                  	// 这个地方的设计非常巧妙。需要仔细斟酌。
                    @Override
                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

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

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

    @Override
    public int getDefaultPort() {
        return protocol.getDefaultPort();
    }

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
      	// 先看下protocol是否是register,如果是则不做过滤器链的包装。
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
      	// 非register类型的protocol都会被过滤器链包装
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
      	// 先看下protocol是否是register,如果是则不做过滤器链的包装。
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
      	// 非register类型的protocol都会被过滤器链包装
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }

    @Override
    public void destroy() {
        protocol.destroy();
    }
}

这段代码,设计非常精良,非常巧妙。需要仔细体会其中的细节。

总结

代码非常重点的注释,我这边在写一遍。

① 我们在filter的实现中会写这么一行代码Result result = invoker.invoke(invocation);

② 这边的invoker对象,就是这边框架中的next对象。

③ 发现了没有,每次开发者的那行代码被执行时,就相当于执行了next.invoke(invocation)

RegistryProtocol比较特殊,对这个类不做filter包装。

我个人觉得。这个地方的设计非常巧妙。需要仔细斟酌。

秉着学习的形态去看这部分代码。我不知道怎么去评价它这种设计与javax.servlet.Filter实现方式的优劣,看到Dubbo Filter的这个处理方式,第一感觉是,我去,还能这么玩。

另外还写了2篇关于Dubbo Filter使用技巧的文章。有兴趣的朋友可以看看。

dubbo微服务调用耗时统计

dubbo微服务之间流水号的隐式传递

你可能感兴趣的:(dubbo)