暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

巧用Dubbo过滤器

codeImport 2020-01-16
1140

前面两篇文章分别介绍了服务导出与引用服务的过程,在整个Dubbo调用链中,Filter是非常重要的一环。Dubbo默认已经提供了部分必须的Filter,同时我们也可以利用Filter实现定制化的功能。本文将介绍如何自定义Filter,以及对其进行源码分析。


1. Filter是什么?

服务提供方和服务消费方调用过程拦截,Dubbo 本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。

画外音:笔者现在所负责的项目就是利用Filter实现了类似于ControllerAdvisor的功能,对Provider端返回的异常进行统一处理。


2. Filter如何配置?

1.自定义的Filter首先要实现Filter接口,该接口只有一个方法invoker需要实现。

@Activate(group = "consumer",order = 1)
@Slf4j
public class HelloConsumerFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
log.info(">>>>>>>>>>>>>Hello:{}","HelloConsumerFilter");
return invoker.invoke(inv);
}
}


@Activate(group = "consumer",order = -5)
@Slf4j
public class HelloConsumerFilter2 implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
log.info(">>>>>>>>>>>>>Hello:{}","HelloConsumerFilter2");
return invoker.invoke(inv);
}
}


@Activate(group = "provider,consumer", order = 0)
@Slf4j
public class HelloCommonFilter implements Filter {
@Override
public 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.HelloConsumerFilter
helloConsumer2=com.example.dubboconsumer.filter.HelloConsumerFilter2
commonFilter=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>() {
@Override
public 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();
@Override
public 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, otherwise
return 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


期热文

文章转载自codeImport,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论