在聊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
去调用下一个filter
。FilterChain
的实例中维护了一个过滤器链的列表。每次调用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 Filter
跟javax.servlet.Filter
很像,至少对开发者来讲是这样的。
不同之处:Dubbo Filter
过滤器链的封装方式和执行方式不太一样。javax.servlet.Filter
可以很明显在ApplicationFilterChain
中知道filters
这个数组,很明显,它维护了一个过滤器链。而Dubbo Filter
是使用了Wapper
的方式。将所有的过滤器一个包着一个。看下面这张图,可能会有些感觉了。
图中:Filter1
包装了Filter2
,Filter2
包装了Filter3
,Filter3
包装了真实的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个包装类,分别是ProtocolFilterWrapper
,ProtocolListenerWrapper
,QosProtocolWrapper
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
RegistryProtocol
,DubboProtocol
在初始化的时候,就会被这3个包装类逐个包装。
看下Protocol
模块的类图
上图类有点多,不过结果很清晰。看下Protocol
的5个子类,分别是
AbstractProtocol
(具体协议实现类都在这个分支上)
ProtocolFilterWrapper
,ProtocolListenerWrapper
,QosProtocolWrapper
(三个包装类)
RegistryProtocol
(这个比较特殊,它算是协议模块的适配器,dubbo服务导出和引入里面有详细说明)
仔细分析结构之后会发现,Protocol
模块并不复杂。
从上述代码可以看到,任何一个Protocol
的子类(除包装类本身)在实例化的时候,都会被3个包装类包装一下,其中一个就是ProtocolFilterWrapper
。那么当运行时在执行Protocol.export
和Protocol.refer
时,就会先执行ProtocolFilterWrapper.export
和ProtocolFilterWrapper.refer
。
什么时候会执行Protocol.export
和Protocol.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微服务之间流水号的隐式传递