dubbo-源码阅读之Filter
时间:2019-11-27
本文章向大家介绍dubbo-源码阅读之Filter,主要包括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 所以这里不会加载 */ if (!((List)names).contains(name) && !((List)names).contains("-" + name) && this.isActive(activate, url)) { exts.add(ext); } } } /** * 排序 */ 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; }
看完之后是不是一切都明朗了。。我们的com.alibaba.dubbo.rpc.Filter 配置 是SPI实现方式 实现动态注入
原文地址:https://www.cnblogs.com/LQBlog/p/11943719.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- js 逗号表达式
- spring动态调用方法
- Spring AOP动态代理原理与实现方式
- 基于注解多数据源解决方案
- Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
- 你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?
- java阻塞队列得实现
- 谈谈如何利用 valgrind 排查内存错误
- 用java写一个死锁
- Runnable和Thread比较
- 使用@ConditionalOnProperty注解
- Java注解Annotation与自定义注解详解
- spring boot 配置多个DispatcherServlet
- 基于Pytorch实现的MASR中文语音识别
- Quartz.NET 配置文件详解