1 dubbo的过滤器链集成进RPC链条的底层原理
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;
}
上面的逻辑还是比较简单的,即:
- (1)先获取所有的Filter,由《【dubbo源码解析】 — dubbo spi 机制之@Activate简介》这篇文章可知,Filter的顺序是按照各个Filter上的Order来指定的。
- (2)将所有 的filter 包装进 invoker 对象中, invoke 方法直接调对应的 filter.invoke
- (3) filter 对象首尾相联, 前一个 filter.invoke 参数, 传入后一个 filter 的 invoker对象
- (4) 最后一个 filter.invoke 参数中, 直接传原始的 invoker 对象
2 dubbo过滤器链封装为Invoker的好处
首先在说其好处之前,肯定要先明白过滤器 (Filter)到底是干什么用的 —》 其实很简单就是 在请求处理前或者处理后做一些通用的逻辑
,如打印日志、异常处理等。
理清了这些之后我们再把dubbo将过滤器链嵌入到 RPC 的调用链条中逻辑用一个简图进行整理一下(以消费端为例):
再用反正法说一下其好处:
- (1)首先通过前面几篇文章应该知道,其实在dubbo内基本所有的组件都被封装成了Invoker,这样的话,各个组件之间的调用方式就变成统一的了,因此将Filter也封装成Invoker便于dubbo的整体性。
- (2)如果不把Filter封装为Invoker,则Filter可能就得散落在其他的Invoker里,这样到底哪个Invoker里需要哪几个Filter就会变得异常难以处理。
- (3)易于整体的扩展性。将Filter封装为Invoker后,这样无论是容错组件、负载均衡组件等都是一个个的Invoker,想要进行其他扩展的话,只需要将这些Invoker进行相应的嵌套封装,然后嵌套到RPC的链条中就好了 —> 可以说为Dubbo的扩展性提供了无限可能!!!
最后在放一张《【dubbo源码解析】— dubbo中Invoker嵌套调用底层原理》文章中给出的 dubbo的RPC调用简图,相信你会更有感触!!!