dubbo-源码阅读之Filter实现原理
最近完成一个需求,使用阿里Arms需要在log里面加上traceId,但是发现dubbo异常 被ExceptionFilter捕获 并打印 打印不出traceI,然后百度搜索如何重写Filter
参考了这篇文章
https://www.jianshu.com/p/7e7076212bd0
重写ExceptionFilter
1.新增一个dubboExceptionFilter类
标红部分 是我改动电脑 其他都是复制原来的ExceptionFilter
@Activate(
group = {"provider"}
)
public class ArmsDubboExceptionFilter implements Filter {
private final Logger logger;
public ArmsDubboExceptionFilter() {
this(LoggerFactory.getLogger(ArmsDubboExceptionFilter.class));
}
public ArmsDubboExceptionFilter(Logger logger) {
this.logger = logger;
}
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
try {
setSpan(invocation);
Result result = invoker.invoke(invocation);
if (result.hasException() && GenericService.class != invoker.getInterface()) {
try {
Throwable exception = result.getException();
if (!(exception instanceof RuntimeException) && exception instanceof Exception) {
return result;
} else {
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?>[] exceptionClassses = method.getExceptionTypes();
Class[] arr$ = exceptionClassses;
int len$ = exceptionClassses.length;
for (int i$ = 0; i$ < len$; ++i$) {
Class<?> exceptionClass = arr$[i$];
if (exception.getClass().equals(exceptionClass)) {
return result;
}
}
} catch (NoSuchMethodException var11) {
return result;
}
this.logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile != null && exceptionFile != null && !serviceFile.equals(exceptionFile)) {
String className = exception.getClass().getName();
if (!className.startsWith("java.") && !className.startsWith("javax.")) {
return (Result) (exception instanceof RpcException ? result : new RpcResult(new RuntimeException(StringUtils.toString(exception))));
} else {
return result;
}
} else {
return result;
}
}
} catch (Throwable var12) {
this.logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + var12.getClass().getName() + ": " + var12.getMessage(), var12);
return result;
}
} else {
return result;
}
} catch (RuntimeException var13) {
this.logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + var13.getClass().getName() + ": " + var13.getMessage(), var13);
throw var13;
} finally {
ArmsUtils.remove();
}
}
/*
* 因为通过arms sdk拿不到traceId(与阿里工程师沟通 貌似是bug 暂时自己再协议头里面获取 并存入线程缓存 供log appender使用)
* @param invocation
*/
public void setSpan(Invocation invocation) {
try {
String sampled = invocation.getAttachment("X-B3-Sampled");
ArmsUtils.setSpan(invocation.getAttachment("X-B3-TraceId"), invocation.getAttachment("EagleEye-RpcID"), invocation.getAttachment("X-B3-SpanId"), sampled != null && sampled.equals("1"));
} catch (Exception e) {
logger.error("写入span异常", e);
}
}
}
2.在/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter新增一个文件com.alibaba.dubbo.rpc.Filter

内容
DubboExceptionFilter=com.biz.core.armslog.ArmsDubboExceptionFilter
3.soa-provider.xml配置
<!-- 延迟暴露服务,表示延迟到Spring容器初始化完成时暴露服务; 不重试 filter自定义一个dubboExceptionFilter -exception表示替换了默认的ExceptionFilter 增加arms日志打印-->
<dubbo:provider delay="-1" retries="0" filter="DubboExceptionFilter,-exception"/>
阅读源码 理解为什么这么写
首先需要理解dubbo SPI的实现原理 前一篇有些
ProtocolFilterWrapper
为dubboFilter的包装类 用来为生成filter执行链
/**
* 为invoker生成filter调用链条 invoker为执行对象
* @param invoker 执行对象
* @param key 为参数名
* @param group 等于@Activate(group = {"provider"})
* @param <T>
* @return
*/
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
final Invoker<T> last = invoker;
/**
* 使用dubbo SPI获取默认和用户自定义的filter
* url为暴露的服务地址
* keyinjvm://127.0.0.1/com.biz.soa.service.promotion.backend.OfflineExtend.PromotionOfflineService?anyhost=true&application=soa-promotion-provider&bind.ip=10.37.129.2&bind.port=23888&default.delay=-1&default.retries=0&default.service.filter=DubboExceptionFilter,-exception&delay=-1&dispatcher=message&dubbo=2.6.2&generic=false&interface=com.biz.soa.service.promotion.backend.OfflineExtend.PromotionOfflineService&methods=downLoadPromotion,updaPromotion,queryPromotionOffline,getOfflineProducts,SavePromotion&pid=81391®ister=false&side=provider&threadpool=fixed&threads=500×tamp=1574842487417
* group为provider (如果是consumer则是consumer)
* */
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
/**
* 这里是循环生成filer调用链条 filter1->filter2->invoke
*/
for(int i = filters.size() - 1; i >= 0; --i) {
final Filter filter = (Filter)filters.get(i);
last = new Invoker<T>() {
public Class<T> getInterface() {
return invoker.getInterface();
}
public URL getUrl() {
return invoker.getUrl();
}
public boolean isAvailable() {
return invoker.isAvailable();
}
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(last, invocation);
}
public void destroy() {
invoker.destroy();
}
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
ExtensionLoader
ExtensionLoader.getExtensionLoader实现
/**
* 根据class获取对应的ExtensionLoader
* @param type
* @param <T>
* @return
*/
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
} else if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
} else if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
} else {
//因为是泛型对应的扩展点 提前初始化了一个对应类型的 ExtensionLoader到 这里就会获取到ExtensionLoader<Filter> 的对象
ExtensionLoader<T> loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));
loader = (ExtensionLoader)EXTENSION_LOADERS.get(type);
}
return loader;
}
}
ExtensionLoader.getActivateExtension
/**
* 获取所有filter 默认的或者用户配置的
* @param url
* @param values
* @param group
* @return
*/
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList();
List<String> names = values == null ? new ArrayList(0) : Arrays.asList(values);
String name;
/**
* 这里是加载默认的filter 如果配置了 -default 将忽略所有默认过滤器
*/
if (!((List)names).contains("-default")) {
this.getExtensionClasses();
Iterator i$ = this.cachedActivates.entrySet().iterator();
while(i$.hasNext()) {
Map.Entry<String, Activate> entry = (Map.Entry)i$.next();
/**
* 获得spi配置的key名字 如dubboSPI配置: exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
*/
name = (String)entry.getKey();
Activate activate = (Activate)entry.getValue();
/**
* 这里匹配Activate的配置的group 只找出provider的
*/
if (this.isMatchGroup(group, activate.group())) {
T ext = this.getExtension(name);
/**
* 因为我们配置了-exception 所以这里不会加载 * isActive如果注解配置了value@Activate(group={"provider"},value={"token"}) 则会在parameter检查是否有传递token 参数 如果有才加入到过滤器
*/
if (!((List)names).contains(name) && !((List)names).contains("-" + name) && this.isActive(activate, url)) {
exts.add(ext);
}
}
}
/**
* 排序 可以看ActivateCompartor.COMPARATOR实现 这个排序实现是根据 注解的order 值来排的
*/
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
/**
* 下面是加载用户的Filter
* <dubbo:provider delay="-1" retries="0" filter="DubboExceptionFilter,-exception"/>
*/
List<T> usrs = new ArrayList();
for(int i = 0; i < ((List)names).size(); ++i) {
name = (String)((List)names).get(i);
//-开头的的表示剔除 不执行逻辑
if (!name.startsWith("-") && !((List)names).contains("-" + name)) {
if ("default".equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = this.getExtension(name);
usrs.add(ext);
}
}
}
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
private boolean isActive(Activate activate, URL url) {
//获得注解桑的value
String[] keys = activate.value();
//如果没有值直接返回true
if (keys.length == 0) {
return true;
} else {
String[] arr$ = keys;
int len$ = keys.length;
label34:
//遍历values
for(int i$ = 0; i$ < len$; ++i$) {
String key = arr$[i$];
//获得参数列表迭代器 参数列表为xml配置哦
Iterator i$ = url.getParameters().entrySet().iterator();
String k;
String v;
do {
do {
if (!i$.hasNext()) {
continue label34;
}
Map.Entry<String, String> entry = (Map.Entry)i$.next();
k = (String)entry.getKey();
v = (String)entry.getValue();
//参数包含了 过滤器信息 同时值不能为空 就加入过滤器
} while(!k.equals(key) && !k.endsWith("." + key));
} while(!ConfigUtils.isNotEmpty(v));
return true;
}
return false;
}
}
看完之后是不是一切都明朗了。。我们的com.alibaba.dubbo.rpc.Filter 配置 是SPI实现方式 实现动态注入
dubbo-源码阅读之Filter实现原理的更多相关文章
- 【Dubbo源码阅读系列】服务暴露之本地暴露
在上一篇文章中我们介绍 Dubbo 自定义标签解析相关内容,其中我们自定义的 XML 标签 <dubbo:service /> 会被解析为 ServiceBean 对象(传送门:Dubbo ...
- 【Dubbo源码阅读系列】之远程服务调用(上)
今天打算来讲一讲 Dubbo 服务远程调用.笔者在开始看 Dubbo 远程服务相关源码的时候,看的有点迷糊.后来慢慢明白 Dubbo 远程服务的调用的本质就是动态代理模式的一种实现.本地消费者无须知道 ...
- 【Dubbo源码阅读系列】服务暴露之远程暴露
引言 什么叫 远程暴露 ?试着想象着这么一种场景:假设我们新增了一台服务器 A,专门用于发送短信提示给指定用户.那么问题来了,我们的 Message 服务上线之后,应该如何告知调用方服务器,服务器 A ...
- 【Dubbo源码阅读系列】之 Dubbo SPI 机制
最近抽空开始了 Dubbo 源码的阅读之旅,希望可以通过写文章的方式记录和分享自己对 Dubbo 的理解.如果在本文出现一些纰漏或者错误之处,也希望大家不吝指出. Dubbo SPI 介绍 Java ...
- Dubbo源码阅读顺序
转载: https://blog.csdn.net/heroqiang/article/details/85340958 Dubbo源码解析之配置解析篇,主要内容是<dubbo:service/ ...
- Android源码阅读-Filter过滤器
Filter 顺便看看,Android中过滤器是怎么实现的? 注释中提到,Filter一般通过继承Filterable实现 具体实现 这是SimpleAdapter出现的一个过滤首字母item的一个过 ...
- Dubbo源码阅读-服务导出
Dubbo服务导出过程始于Spring容器发布刷新事件,Dubbo在接收到事件后,会立即执行服务导出逻辑.整个逻辑大致可分为三个部分,第一部分是前置工作,主要用于检查参数,组装URL.第二部分是导出服 ...
- dubbo源码阅读之负载均衡
负载均衡 在之前集群的文章中,我们分析了通过监听注册中心可以获取到多个服务提供者,并创建多个Invoker,然后通过集群类如FailoverClusterInvoker将多个Invoker封装在一起, ...
- dubbo源码阅读之自适应扩展
自适应扩展机制 刚开始看代码,其实并不能很好地理解dubbo的自适应扩展机制的作用,我们不妨先把代码的主要逻辑过一遍,梳理一下,在了解了代码细节之后,回过头再来思考自适应扩展的作用,dubbo为什么要 ...
随机推荐
- 关于C++中的非静态类成员函数指针
昨天发现了一个问题,就是使用对类中的非静态成员函数使用std::bind时,不能像普通函数一样直接传递函数名,而是必须显式地调用&(取地址),于是引申出我们今天的问题:非静态类成员函数指针和普 ...
- java命令-jstat/ javap
jstat命令对应用程序资源和性能进行实时监控 常用参数列举如下: 1. jstat -class pid 显示加载class的数量.所占空间.所耗时间等信息 2.jstat -compiler pi ...
- 【串线篇】SpringMVC九大组件
SpringMVC中的Servlet一共有三个层次,分别是HttpServletBean.FrameworkServlet和 DispatcherServlet. HttpServletBean直接继 ...
- List Comprehension ()(一)
>>> L = [1,2,3,4,5] >>> L = [x+10 for x in L] >>> L [11, 12, 13, 14, 15] ...
- GA来源分析
网页中广告素材分为:文字,图片和FLASH三种.针对这三种素材,2种有无参数的情况,新旧版GA收集到的结果为: 提醒:FLASH素材如果不加参数收集不到来源: 具体GA参数如下: 可参考:https: ...
- HDU-4609(FFT/NTT)
HDU-4609(FFT/NTT) 题意: 给出n个木棒,现从中不重复地选出3根来,求能拼出三角形的概率. 计算合法概率容易出现重复,所以建议计算不合法方案数 枚举选出的最大边是哪条,然后考虑剩下两条 ...
- C/C++ C++ 11 std::move()
{ 0. C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意 是想把参数push_back进去就行 ...
- 【Flutter学习】页面布局之基础布局组件
一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Padding.Center.Flex.Row.Colum.ListView.GridView.按照< ...
- OC学习篇之---@class关键字的作用以及#include和#import的区别
前一篇文章说到了OC中类的三大特性:http://blog.csdn.net/jiangwei0910410003/article/details/41707161今天我们来看一下在学习OC的过程中遇 ...
- JS基础入门篇(二十七)—BOM
虽然上次写到js基础篇(二十四),这次直接写到(二十七).是为了提醒自己中间有几篇没写.特此说明一下啊. 1.window.open() 使用a标签呢,点击一下a标签页面才会跳转,有时候我们需要做的操 ...