前面两篇文章分别介绍了服务导出与引用服务的过程,在整个Dubbo调用链中,Filter是非常重要的一环。Dubbo默认已经提供了部分必须的Filter,同时我们也可以利用Filter实现定制化的功能。本文将介绍如何自定义Filter,以及对其进行源码分析。
1. Filter是什么?
服务提供方和服务消费方调用过程拦截,Dubbo 本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。
画外音:笔者现在所负责的项目就是利用Filter实现了类似于ControllerAdvisor的功能,对Provider端返回的异常进行统一处理。
2. Filter如何配置?
1.自定义的Filter首先要实现Filter接口,该接口只有一个方法invoker需要实现。
@Activate(group = "consumer",order = 1)@Slf4jpublic class HelloConsumerFilter implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {log.info(">>>>>>>>>>>>>Hello:{}","HelloConsumerFilter");return invoker.invoke(inv);}}@Activate(group = "consumer",order = -5)@Slf4jpublic class HelloConsumerFilter2 implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {log.info(">>>>>>>>>>>>>Hello:{}","HelloConsumerFilter2");return invoker.invoke(inv);}}@Activate(group = "provider,consumer", order = 0)@Slf4jpublic class HelloCommonFilter implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {log.info(">>>>>>>>>>>>>Hello:{}","HelloCommonFilter");return invoker.invoke(inv);}}
2.在项目的META-INFO/services/文件夹中创建org.apache.dubbo.rpc.Filter文件 Dubbo会在此文件中查找Filter,文件内容如下
helloConsumer=com.example.dubboconsumer.filter.HelloConsumerFilterhelloConsumer2=com.example.dubboconsumer.filter.HelloConsumerFilter2commonFilter=com.example.dubboconsumer.filter.HelloCommonFilter
执行完上面两步之后,当启动服务时,Dubbo就会将这3个过滤器加入调用链。
3. Filter如何区分类型?
使用group参数区分
•group="consumer":该Filter只会加入Consumer端调用链;•group="provider":该Filter只会加入Provider端调用链;•group="consumer,provider":该Filter会加入两端的调用链;•group的值为空则该过滤器不会加入任何调用链。
4. Filter如何控制执行顺序?
使用order参数进行控制,默认是0,可以为负数,该值越小,越先执行。
5. 如何只使用特定的Filter?
如果只想使用特定的Filter,可以在@Reference,@Service中使用filter参数进行控制
@Reference(filter={"-default"}):剔除所有Filter;
@Reference(filter={"-helloConsumer"}):剔除HelloConsumerFilter;
6. Filter源码分析
在Consumer端进行引用服务时,会调用ProtocolFilterWrapper构造调用链,Dubbo会调用ExtensionLoader获取到上述配置的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(), 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>() {@Overridepublic Result invoke(Invocation invocation) throws RpcException {//。。return asyncResult;}};}}return new CallbackRegistrationInvoker<>(last, filters);}
从特定目录读取指定配置文件
private Map<String, Class<?>> loadExtensionClasses() {this.cacheDefaultExtensionName();Map<String, Class<?>> extensionClasses = new HashMap();this.loadDirectory(extensionClasses, "META-INF/dubbo/internal/", this.type.getName());this.loadDirectory(extensionClasses, "META-INF/dubbo/internal/", this.type.getName().replace("org.apache", "com.alibaba"));this.loadDirectory(extensionClasses, "META-INF/dubbo/", this.type.getName());this.loadDirectory(extensionClasses, "META-INF/dubbo/", this.type.getName().replace("org.apache", "com.alibaba"));this.loadDirectory(extensionClasses, "META-INF/services/", this.type.getName());this.loadDirectory(extensionClasses, "META-INF/services/", this.type.getName().replace("org.apache", "com.alibaba"));return extensionClasses;}
根据group判断是否要加入当前调用链
if (this.isMatchGroup(group, activateGroup) && !((List)names).contains(name) && !((List)names).contains("-" + name) && this.isActive(activateValue, url)) {exts.add(this.getExtension(name));}
根据order进行排序
if (isMatchGroup(group, activateGroup)&& !names.contains(name)&& !names.contains(REMOVE_VALUE_PREFIX + name)&& isActive(activateValue, url)) {exts.add(getExtension(name));}}//使用ActivateComparator进行排序exts.sort(ActivateComparator.COMPARATOR);public class ActivateComparator implements Comparator<Object> {public static final Comparator<Object> COMPARATOR = new ActivateComparator();@Overridepublic int compare(Object o1, Object o2) {if (o1 == null && o2 == null) {return 0;}if (o1 == null) {return -1;}if (o2 == null) {return 1;}if (o1.equals(o2)) {return 0;}Class<?> inf = findSpi(o1.getClass());ActivateInfo a1 = parseActivate(o1.getClass());ActivateInfo a2 = parseActivate(o2.getClass());if ((a1.applicableToCompare() || a2.applicableToCompare()) && inf != null) {//。。。}int n1 = a1 == null ? 0 : a1.order;int n2 = a2 == null ? 0 : a2.order;// never return 0 even if n1 equals n2, otherwisereturn n1 > n2 ? 1 : -1;}
7. 总结
•过滤器可以加入Consumer端,也可以加入Provider端;•使用group进行类型区分,如果不设置,则该过滤器不起任何作用;•使用order控制过滤器调用顺序;
References
[1]
调用拦截扩展: http://dubbo.apache.org/zh-cn/docs/dev/impls/filter.html
[2]
github: https://github.com/apache/dubbo
近期热文




