2020 年写了很多事故解决的文章,并不是我绞尽脑汁想出来的,而是真的遇到了这些问题。通过文章的方式记录下来,分享出去,才有意义。
事故背景
首先看下面的图吧,这是我从 cat 上截的图。
![ad101155474fc4bb80807241d61413fe.png](https://img-blog.csdnimg.cn/img_convert/ad101155474fc4bb80807241d61413fe.png)
可以看到是一个 Rpc 调用的错误,从错误中我们只能分析出这个 Rpc 的请求成功了,并且返回了,因为都走到了反序列化这步。
最后是在创建 DTO 对象的时候报错了,Could not initalize class xxxxx.DTO说明了这一点。
作为一个调用方,虽然看到了明确的错误,但还是要本着严谨的态度去排查问题,还是先确认服务提供者到底有没有问题,跟同事确认了,服务提供方没问题,通过 telnet 可以正常 invoke。
好了,到这为止就把背景交代清楚了,能不能将这个潜藏的 Bug 找出来就各显身手吧。
arthas 大显身手
要想效率高,那必须得有好用的工具呀!arthas 挺身而出,都毛遂自荐了,不用白不用。
首先使用 sc 命令查看 JVM 已加载的类信息,就看这个不能实列化的类到底有没有被成功加载。
sc -d 类全路径 (打印类的详细信息)
![3fd9c09878aa4b461f0eaf342446ee1e.png](https://img-blog.csdnimg.cn/img_convert/3fd9c09878aa4b461f0eaf342446ee1e.png)
类的信息都被打印出来了,足以证明这个类被加载了。
然后打印下类里面的字段,看看有没有丢失什么的
sc -d -f 类全路径 (打印出类的Field信息)
![e3521725300241319caffcebc9464978.png](https://img-blog.csdnimg.cn/img_convert/e3521725300241319caffcebc9464978.png)
居然报错了,错误还跟我们之前在 cat 中看到的一模一样,这边也是要是创建对象,然后反射获取所有字段信息,由于不能创建对象,直接报错了。
就这么结束了吗?怎么可能,还没下班呢,接着走下去。。。。
现在我开始怀疑这个 class 是不是有问题,然后就开始用 arthas 的另一个命令 jad 来反编译。
通过 jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码,便于我们理解业务逻辑,也能让我们知道代码跟本地的到底是不是一致。
jad --source-only 类全路径
执行完后,什么也没输出,我一度怀疑这个命令是不是我用错了,然后我试了下 jad --source-only java.lang.String 发现命令没问题,就是那个 class 有问题。
这时我想起还有一个 redefine 命令可以用于加载外部的.class 文件,看看能不能加载进来。于是我将 lib 目录里面依赖的 jar 包解压了,然后用 redefine 去加载那个不能反编译的 class。
![464f2f157889ca2c55df059bde54bd2b.png](https://img-blog.csdnimg.cn/img_convert/464f2f157889ca2c55df059bde54bd2b.png)
居然告诉我是一个无效的 class,尝试多次都无法让这个 class 现出庐山真面目。
最后没办法,只能将这个 class 弄到本地,拖入 IDEA 中反编译,对比了下代码,跟 git 仓库里面的一模一样,也就不存在 jar 包损坏的问题。
即将揭开真相
到目前为止,有效的线索如下:
- class 已加载,但是无法实例化
- 通过本地反编译,代码是完整的
越在这种没有思路的情况下越要静下心来思考,于是再次看了一遍源码,发现这个类中有引用一个外部的自定义异常类。
然后我用 sc -d 去查看这个类的信息,告诉我不存在,终于明白了。
![e33232f0388e5fdc0573e1aa363c6248.png](https://img-blog.csdnimg.cn/img_convert/e33232f0388e5fdc0573e1aa363c6248.png)
看上面这张图,项目 A 依赖了 API,API 中依赖了 Common,Common 中又依赖了很多其他的三方 Jar 包。
由于项目 A 和 Common 中依赖的三方 Jar 包冲突了,所以项目 A 中之前就简单粗暴的把 Common 给排除了,冲突是解决了。
在进行 RPC 调用的时候,请求的数据响应回来后需要反序列化成对象,这个时候去创建对象失败了,因为类中依赖了某个外部的类,但在当前项目中没有加载进来,所以就报错了。
总结
这次的问题归根到底还是没有想到一个 API 会依赖其他的模块,本身 API 作为 RPC 调用客户端就应该简洁。
其实在做 exclusion 的时候应该只 exclusion 有冲突的三方 Jar,不应该将整个 Common 都 exclusion 掉。
最后就是合理的利用方便快速的工具帮助我们快速的排查问题,arthas 就是这个好帮手,通过 arthas 我们可以进一步排除程序启动后加载的 class 有没有问题,进一步缩小范围。
技术交流可以扫下面图片加我微信。
欢迎关注个站
往期回顾
微服务自动化部署CI/CD
如何利用k8s拉取私有仓库镜像
个站建设基础教程
ArrayList、LinkedList 你真的了解吗?
大佬整理的mysql规范,分享给大家
如果张东升是个程序员
微服务架构设计之解耦合
浅谈负载均衡
Oauth2的认证实战-HA篇
Oauth2的授权码模式《上》
浅谈开发与研发之差异
浅谈 Java 集合 | 底层源码解析
基于 Sentinel 作熔断 | 文末赠资料
基础设施服务k8s快速部署之HA篇
今天被问微服务,这几点,让面试官刮目相看
Spring cloud 之多种方式限流(实战)
Spring cloud 之熔断机制(实战)
面试被问finally 和 return,到底谁先执行?
Springcloud Oauth2 HA篇
Spring Cloud Kubernetes之实战一配置管理
Spring Cloud Kubernetes之实战二服务注册与发现
Spring Cloud Kubernetes之实战三网关Gateway
关注公众号,回复入群,获取更多惊喜!公众号(天山六路折梅手)里回复 ES、Flink、Java、Kafka、MQ、ML、监控、大数据、k8s 等关键字可以查看更多关键字对应的文章。
![762e427a012aab6ca7d1afd1632cb623.gif](https://img-blog.csdnimg.cn/img_convert/762e427a012aab6ca7d1afd1632cb623.gif)
点击 "damon8.cn" 获取更好的阅读体验!
❤️给个「在看」,是对我最大的支持❤️