谁再悄咪咪的吃掉异常,我上去就是一 JIO
这图太魔性了啊
这次分享一下上个月碰到的离奇的问题。一个简单的问题,硬是因为异常被悄咪咪吃掉,过关难度直线提升,导致小黑哥排查一个晚上。
这个美好的晚上,本想着开两把 LOL 无限火力,在召唤师峡谷来个五杀的~
哎,就这样没了啊!我知道,你们一定能理解这种五杀被抢的感觉~
下次,真的,谁再让我看到悄咪咪的吃掉异常,我真的要上去一 Jio 了!
好了,本文可不是水文,看完本篇文章,你可以学到以下知识点:
- Arthas 排查技巧
- 啥是 NoClassDefFoundError
- Dubbo 异常内部处理方式
好了,同学们,打开小本子,准备记好知识点~
起因
我们有个业务系统,应用之间调用链如下所示:
A 应用是业务发生起始应用,在这个应用中将会根据一定规则选择最后的通讯渠道 C,然后将这个渠道标识传递给 B 应用。
B 应用的功能类似网关,这个应用将会根据 A 应用传递过来的渠道标识,将会请求路由下发到具体的 C 应用,起到服务路由的功能。
C 应用是与外部应用交互的应用,我们将其称为渠道通讯机。
假设一次业务中,A 应用根据规则选择 C2 的渠道标识,然后传递给 B 应用。B 应用根据这个标识选择使用 C2 进行通讯,最后 C2 调用外部应用完成一次业务调用。
上述所有应用都基于 Dubbo 进行远程通讯。
介绍完业务的基本情况,现在我们来看下到底发生了啥事。
一次业务需求中,需要改动 C2 应用,这次改动功能点真的很小,很快就完成了。小黑哥想着闲着也是闲着,于是就把之前 C2 应用中打印的日志中一些没有脱敏的信息,进行脱敏处理。
由于之前日志框架脱敏处理存在一些问题,于是就将日志框架从 Log4j 升级为 LogBack。升级之后,为了防止不同日志框架中之间的产生冲突,于是使用 IDEA Maven Helper 插件,统一将应用中所有的 Log4j 相关依赖都给排除了。
改动完成之后,将 C2 应用发布到测试环境,再次从 A 应用发起测试, B 应用返回异常提示未找到 C2 应用。
B 应用业务代码类似如下:
public Response pay(Request req) { try { if (!isSupport(req.getChnlCode())) { return new Response("ERROR", "未找到相关渠道应用"); } return doPay(req); } catch (Exception e) { return new Response("ERROR", "未找到相关渠道应用"); }}
正常情况下,若是配置存在问题,B 应用将会返回未找到具体渠道,请求也会在 B 应用结束,不会调用到 C2 应用(也没办法调用)。
然而此次配置什么都没问题, 而且最诡异的是 C2 应用居然收到了请求,并且成功处理了业务请求。
排查问题
由于 B 应用异常处理时,将异常吃掉了,我们没办法得知这个过程到底发生了啥事,所以第一要紧的事获取异常信息。
最简单的办法就是,将 B 应用改造一下,加入打印异常日志。不过当时比较懒,不想改造应用,就想获取异常信息,于是想到使用 Arthas[1]。
Arthas 排错技巧
Arthas 是Alibaba开源的Java诊断工具,这里就不再详细介绍这个工具,主要讲下这次排错用到的命令watch[2]。
watch 命令可以方便观察指定方的调用情况,可以具体观察方法的返回值、抛出异常、入参,另外还可以通过 OGNL表达式查看对应的变量。
这里我们主要为了查看方法抛出的异常信息,执行命令如下:
watch com.dubbo.example.DemoService doPay -e -x 2 '{params,throwExp}'
上述命令将会在方法异常之后观察方法的入参以及异常信息。
注意,我们需要查看 doPay 方法,而不是 pay 方法。这是因为 pay方法中我们将异常捕获,不太可能会抛出异常哦~
异常信息如下所示:
真正引起此次错误的异常信息为:
java.lang.NoClassDefFoundError: Could not initialize class xx.xxx.xx.GELogger
由于此次 B 应用不存在改动,所以推测这个异常实际发生在 C2 应用,于是在 C2 应用处再次使用 Arthas watch 命令,同样观察到相同的错误信息。
NoClassDefFoundError
NoClassDefFound,从名字上我们可以推测是因为类不存在,从而引发的这个错误。按照这个思路,我们首先可以简单查看一下 B 应用中是否存在 GELogger 相关类。
查看 B 应用相关依赖包,从中发现了这个类文件,这说明这个类确实存在。
在 IDEA 反编译查看 GELogger类相关源码,从中发现了问题。
private static Logger logger;static { System.out.println("static init"); logger = Logger.getLogger(NoClassDefFoundErrorTestService.class); System.out.println("Logger init success");}
GELogger存在一个静态代码块,用于初始化一个 org.apache.log4j.Logger日志类。
然后在上面改动中,全部的 Log4j依赖都被排除了,所以这里运行时应该会抛出另外一个找到 org.apache.log4j.Logger 错误。
执行以下代码,模拟抛错过程。
System.out.println("模拟第一次 Error");try { NoClassDefFoundErrorTestService noClassDefFoundErrorTestService=new NoClassDefFoundErrorTestService();} catch (Throwable e) { e.printStackTrace();}System.out.println("模拟第二次 Error");try { NoClassDefFoundErrorTestService noClassDefFoundErrorTestService=new NoClassDefFoundErrorTestService();} catch (Throwable e) { e.printStackTrace();}
异常信息如下所示:
异常信息
第一次创建 NoClassDefFoundErrorTestService实例时,Java 虚拟机读取加载时,将会初始化静态代码块时。由于 org.apache.log4j.Logger类不存在,静态代码块执行异常,从而导致类加载失败。
第二次再创建 NoClassDefFoundErrorTestService 实例时,Java 虚拟机不会再次读取加载,所以直接返回了以下异常。
java.lang.NoClassDefFoundError: Could not initialize class com.dubbo.example.NoClassDefFoundErrorTestService
找到问题真正原因,解决办法也很简单,直接排除 GELogger 所在依赖包。
Dubbo 内部异常处理
虽然问题到此解决了,但是这里还有一个疑问,为何 C2 应用发生了异常,却没有相关错误日志,并且 C2 业务逻辑也正常处理完成。
这就要说到 Dubbo 内部异常错误处理方式,上面 GELogger 其实作用在一个 Dubbo 自定义 Filter 中,用来记录结果,模拟代码如下:
@Activate( group = {"provider", "consumer"})public class ErrorFilter implements Filter { @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { Result result = invoker.invoke(invocation); NoClassDefFoundErrorTestService noClassDefFoundErrorTestService=new NoClassDefFoundErrorTestService(); // 处理业务逻辑 return result; }}
这个自定义 Filter 中首先执行 invoker 方法,这个方法将会调用真正的业务方法,这就是为什么 C2 应用逻辑是正常处理完成。
业务方法处理完成之后,然后执行后续逻辑。由于 NoClassDefFoundErrorTestService将会抛出 Error,最终这个 Error,将会在 HeaderExchangeHandler#handleRequest 被捕获,然后将会把相关异常信息返回给调用 Dubbo 消费者。
Dubbo 2.7
而在 Dubbo 消费者接受到服务提供者返回信息之后,将会在 DefaultFuture#doReceived转化成 RemotingException。
dubbo consumer 2.7
而 RemotingException 最终将会在 FailoverClusterInvoker#doInvoke 转换成 RpcException返回给业务代码。
总结
好了,说了这么多,总结一下本文知识点
- 异常捕获之后,一定要记得打印日志,并且要记得输出堆栈信息。
- 运行时类不存在,将会导致 NoClassDefFoundError,类加载过程失败,也会导致 NoClassDefFoundError。
- 对外提供的二方包,最好不要依赖特定日志框架,如 Log4j,Logback 等,应该使用 Slf4j 框架。
参考资料
[1] Arthas: https://alibaba.github.io/arthas/index.html
[2] watch: https://alibaba.github.io/arthas/watch.html
[3] 当Dubbo遇上Arthas:排查问题的实践: https://dubbo.apache.org/zh-cn/blog/dubbo-meet-arthas.html
[4] java.lang.NoClassDefFoundError 的解决方法一例: https://www.codelast.com/原创-java-lang-noclassdeffounderror-的解决方法一例/
[5] noclassdeffounderror-could-not-initialize-class-error: https://stackoverflow.com/questions/1401111/noclassdeffounderror-could-not-initialize-class-error
谁再悄咪咪的吃掉异常,我上去就是一 JIO相关推荐
- c++ 外部组件发生异常_谁再悄咪咪的吃掉异常,我上去就是一 JIO
又到周末了,周更选手申请出站~ 这图太魔性了啊 这次分享一下上个月碰到的离奇的问题.一个简单的问题,硬是因为异常被悄咪咪吃掉,过关难度直线提升,导致小黑哥排查一个晚上. 这个美好的晚上,本想着开两把 ...
- 信息学奥赛C++语言:咪咪的智慧
[题目描述] 咪咪是一只聪明的小老鼠(什么什么?谁规定老鼠就不能叫咪咪了?真是!),她正在四处找水喝呢-- 她发现了一些水罐,里面都有水.她趴在每个水罐口上都试了一遍,结果仍然一口水没喝到--这些水罐 ...
- OSChina 周一乱弹 ——渴望咪咪还是渴望力量,都能给你
2019独角兽企业重金招聘Python工程师标准>>> Osc乱弹歌单(2017)请戳(这里) [今日歌曲] @梦朝思夕 :分享张小九的单曲<南方 南方>: <南方 ...
- 微信公开课讲师黄咪咪:熟读规则,玩转小游戏
大家好,我是微信小游戏产品经理黄咪咪.先简单地做一个小调查,在场的开发者注册过小游戏的,麻烦举手.有被线上处罚过的开发者举手. 我们已经和大家进行了非常多的互动.打过非常多的交道,未来也会有更多的打交 ...
- 自定义切面会吃掉异常,导致事务不生效的问题。
问题描述 我们都知道,我们定义一个切面,然后绑定一个切点后,这个切面就能在合适的时间自动切入切点.对于@AfterThrowing和@Around,我们可以再切面中捕获异常,处理异常. 我们也知道,我 ...
- 破云里步重华为什么说自己姓薛_都说虎毒不食子,但猫咪为什么会吃掉自己的孩子呢...
昨天收到一位网友的私信说:"猫妈妈竟然吃掉自己刚生出的小猫,这是什么情况"嗯哼!是不是感觉很不可思议,俗话说:"虎毒不食子"可是猫咪作为这么可爱的宠物,为什么会 ...
- 【新年快乐】嗷,我的2020年,就这样悄咪咪地溜走了
写在最前面 「指缝很宽.时间太瘦.这不,马上2020年就要一溜烟地结束了.」 而我也就在昨天,大年30的前一天,结束了手上所有的工作和任务,开启了时间并不长.却也很普通的春节假期生活.早上六点就醒了, ...
- 我悄咪咪告诉你:罩杯越小的妹子倾向买越贵的内衣~~Python爬取京东9000条内衣销售数据之数据关联度分析
将爬取的9000条内衣销售数据整理清洗后,基于Apriori关联算法,针对"罩杯和消费价格倾向这两个元素有无关系"这个问题进行分析 上一篇用数据库清洗数据,点这里 再上一篇爬取数据 ...
- DeepMind悄咪咪开源三大新框架,深度强化学习落地希望再现
作者 | Jesus Rodriguez 译者 | 夕颜 出品 | AI科技大本营(ID:rgznai100) [导读]近几年,深度强化学习(DRL)一直是人工智能取得最大突破的核心.尽管取得了很多进 ...
最新文章
- 【linux】串口编程(二)——非阻塞接收
- 从云端到边缘 AI推动FPGA应用拓展
- java 本地 交互图_Java与UML交互图
- java map string stu_Java Map遍历的三种方式 | 学步园
- OpenCV在图像中寻找轮廓
- 不依赖任何系统API,用c语言实现gbk/utf8/unicode编码转换
- 人工智能时代来临,还需要那么多人吗?
- php中数组下标,PHP数组介绍_php
- 前后端分离项目中,前端请求的常见问题
- Android 网络评分机制
- MAC安装向日葵软件步骤
- DAOS 源码解析之 daos_array
- 三星支付存在漏洞可导致黑客进行交易劫持
- CAD绘图时怎么查看所绘制图形两点间的距离?
- 开灯问题_谁会一直开灯
- jvisualvm 连接 docker 监控springboot jvm
- 百度在线html,百度编辑器-Ueditor
- swift3.0 GCD
- 什么是公共语言运行时(CLR)
- excel怎么把竖排变成横排_如何优雅的使用Excel作出高大上的图片
热门文章
- 高通音频驱动录音流程(二)
- 在Ubuntu中设置中文输入法
- web页面 新消息提示音
- python分布式邻居节点通讯_P2P网络及节点发现机制
- 数据分析-PART0--数据分析综合
- 用Python-tkinter完成文件对话框图片选择并显示
- SSM登陆验证之过滤器实现
- 知识管理系统:从隐性知识到显性价值
- Discuz!论坛助手 [原创] [服务端+客户端]插件 人气提升/贴子监控/批量顶贴/来贴提醒/编辑辅助工具 1.0(附演示)
- 超级简单进销存-立项