本文对应源码地址:https://github.com/nieandsun/dubbo-study
Filter(过滤器) 在很多框架中都有使用过这个概念, 基本上的作用都是类似的, 在请求处理前或者处理后做一些通用的逻辑, 而且 Filter 可以有多个, 支持层层嵌套。
Dubbo 的 Filter 实现入口是在ProtocolFilterWrapper
, 因为ProtocolFilterWrapper 是 Protocol 的包装类, 所以会在 SPI 加载Extension 的时候被自动包装进来。
这里简单提一下,其实我在《【dubbo源码解析】 — dubbo spi 机制(@SPI、@Adaptive)详解》、《【dubbo源码解析】 — dubbo spi 机制之@Activate简介》这两篇文章的开头都提到过一句话 —
dubbo 要求SPI扩展点的实现类必须要有一个无参构造,除了Wrapper实现类之外
。那这个Wrapper实现类是怎么回事呢???其实是这样的,以ProtocolFilterWrapper为例:
- 首先Protocol要是dubbo的一个扩展点 —> 即Protocol接口上被@SPI注解标记
- 其次Wrapper类要有如下的数据结构 ,
- 那么通过SPI机制选定的
最终生效的实现类
会被包装到(或者set到)这个Wrapper类里 —》 也就是说你以为你获取的是你通过SPI选择的具体的实现类,其实不并不是 —> 而是一个包装了你选择的实现类的Wrapper类。当然最后不得不提的一点是这个Wrapper类也要像其他扩展接口的实现类一样在MATE-INF/dubbo文件夹下进行指定。
当然 filter 要发挥作用, 必定还是要在嵌入到 RPC 的调用链条中 —> 你应该可以马上反应过来, 嵌入的办法就是包装成一个个的Invoker。
那它到底是怎么搞得呢?其实dubbo是这么玩的:
ProtocolFilterWrapper 作为包装类, 会成为其它 protocol 的修饰加强外层。 当调用 protocol 的 export 和 refer 方法, 它首先会调用ProtocolFilterWrapper 类的这两个方法。
ProtocolFilterWrapper类中暴露服务的代码如下:
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
ProtocolFilterWrapper类中引入服务的代码如下:
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
可以看到, 两者原来的 invoker 对象, 都由 buildInvokerChain 做了一层包装。来看一下 filterChain 的逻辑:
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
//通过SPI获取到所有的Filter,注意这里用到的是getActivateExtension --->对应的注解为@Activate
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);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
@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;
}
上面的逻辑还是比较简单的,即:
首先在说其好处之前,肯定要先明白过滤器 (Filter)到底是干什么用的 —》 其实很简单就是 在请求处理前或者处理后做一些通用的逻辑
,如打印日志、异常处理等。
理清了这些之后我们再把dubbo将过滤器链嵌入到 RPC 的调用链条中逻辑用一个简图进行整理一下(以消费端为例):
再用反正法说一下其好处:
最后在放一张《【dubbo源码解析】— dubbo中Invoker嵌套调用底层原理》文章中给出的 dubbo的RPC调用简图,相信你会更有感触!!!