一、首先讲讲工程背景
我们的工程是Kotlin与Java混编的,再附加偶尔写写Groovy,在团队中不断熟悉后发现各语言在编程中各有利弊。
● Java就不说了,阿里在国内Java应用上堪称鼻祖,集团各种工具对Java的支持都比较完备;
● Kotlin有很多语法上的优势,同样的代码Java 10行,Kotlin可能只需要5行,此外支持协程(coroutines),编写非阻塞异步代码看起来像是同步的,能很好地处理IO密集型任务。但是Kotlin毕竟非正统,集团很多工具对Kotlin的支持性比较一般;
● groovy的语法规则更加特殊,工程里用的不是特别多,所以基本都是需要开发时用大模型去解决,没有特意去研究;
二、问题现象
突然有一天组里同学告诉我预发环境(开发-测试-预发/灰度/UAT-生产)有大量报错,是不是我改了架构层面的代码引起的。我心想肯定不是啊,我这是增量编程,并没有改动太多原来的代码。
但是处于谨慎起见,还是把分支踢掉吧,重新部署了一下,发现同样的问题在工程重启一小段时间后又发生了。
下面是问题报错,这会导致工程运行时大部分涉及反序列化的链路中断,而工程到处用了FastJson,影响面可想而知。
三、排查过程
- 怀疑FastJson版本
既然是FastJson的抛错,那是不是有人改动了工程依赖,于是开始查pom文件的改动,一时也想不到别的原因可能导致该报错。
查了一圈并没有发现可疑的地方,于是上AppInsights中看了一下FastJson相关类的加载情况,发现这里有个1.2.68_noneautotype版本的包,而工程其实统一用的是1.2.83_noneautotype,会不会是这里引起的呢?
后来登到线上机器对比了一下发现没有这个版本,那有可能就是别的包引入的,于是看了一下出问题module的pom文件,发现果然有这个版本的FastJson,再定位了下发现是rass-sdk-core引入了这个包,scope为compile,打包时会将这个包编译进工程中。
很果断的将该包中的FastJson排掉,那问题铁定解决了,观察了日志发现没有再抛错,我就没再管。到了下午同学又来反馈有问题,我一看还真是,难道是搞错了。
再次对比了一下预发和线上,发现还真是我搞错了,线上也有1.2.68_noneautotype。
我有点郁闷,为了定位这个bug,翻遍了预发每个人的分支,为了排包前后部署过很多次。此时这个问题已经困扰了我1天多了,想撂挑子放弃,到时候谁发线上时注意下好了。
但又一想这是我们辛苦维护的庞大工程,不能因为这种莫名其妙的问题引发大面积故障,到时候辛苦半年多白干(上价值,给自己增加点动力)。理智告诉我哪怕手头的需求先放一放,还得继续查,虽然只是预发。
- 怀疑kotlin相关依赖
跟同学确认了JDK近期也没有变动后,接下来开始到处翻资料。
大概能确认是反序列化Kotlin的data class实体时出现的问题。类似下面这种结构
GitHub上关于该问题的讨论也比较多,总结一下解法有:
1.FastJson和kotlin版本不兼容
2.工程中需要引入kotlin-reflect
3.需要对data class的非空字段默认初始化
4.……
第三个字段初始化问题不太可能,因为受影响的class从来没改动过,不可能好端端出问题。估计问题应该还是出在工程环境上。
于是去工程里看了一下kotlin-reflect依赖,仍然是正常引入的。
又去看了几遍近期所有分支的所有改动,也没有任何相关变动,慢慢地开始头麻,环境问题一向最难的。