转载:https://www.jianshu.com/p/6dd76ce7338f
0 前言
对于Java WEB应用来说,Spring的Filter可以拦截WEB接口调用,但对于Dubbo接口,Spring的Filter就不起作用了。
Dubbo中的Filter实现是 专门为服务提供方和服务消费方调用过程进行拦截,Dubbo本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,但请注意其对性能的影响。
所以,在实际业务开发中,使用最多的可能就是对Filter接口进行扩展,在服务调用链路中嵌入我们自身的处理逻辑,如日志打印、调用耗时统计等。
Dubbo官方针对Filter做了很多的原生支持,目前大致有20来个吧,包括我们熟知的RpcContext,accesslog功能都是通过filter来实现了,下面一起详细看一下Filter的实现。
1 构造Filter链
Dubbo的Filter实现入口是 在ProtocolFilterWrapper,因为ProtocolFilterWrapper是Protocol的包装类,所以会在加载的Extension的时候被自动包装进来(理解这里的前提是 理解Dubbo的SPI机制 ),该封装器实现了Protocol接口,并提供了一个参数类型为Protocol的构造方法。Dubbo依据这个构造方法识别出封装器,并将该封装器作为其他Protocol接口实现的代理。
接下来,我们看一下ProtocolFilterWrapper中是如何构造Filter链:
public class ProtocolFilterWrapper implements Protocol {
private final Protocol protocol;
// 带参数构造器,ExtensionLoad通过该构造器识别封装器
public ProtocolFilterWrapper(Protocol protocol){
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = protocol;
}
public int getDefaultPort() {
return protocol.getDefaultPort();
}
// 对提供方服务暴露进行封装,组装filter调用链
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
// 向注册中心发布服务的时候并不会进行filter调用链
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
// 对消费方服务引用进行封装,组装filter调用链
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 向注册中心引用服务的时候并不会进行filter调用链
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
public void destroy() {
protocol.destroy();
}
// 构造filter调用链
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