踩坑一则复杂对象(领域实体)序列化失败问题。
归因:测试环境Redis
连接挂掉,同时对DDD
领域实体序列化,JSON.toJSON()
封装了异常,导致关键异常栈丢失,后经过正向逆向分析找到了问题原因与解法。
背景
11.14号,QA给我反馈了一则问题:
测试环境,添加指纹密码时,会报tojson error
:
分析
根据trace
日志,可以直接定位到报错的源码位置:
|
|
其中报错代码示例,password
是这个服务的一个核心领域实体:
|
|
但是这里的异常栈并没有把最原始的异常打印出来,原因是JSON.toJSON
中对异常进行了包装:
现在可以确定的一点是,在 SampleEncoder
中,对领域实体 Password
使用fastjson
库序列化为字符串时,发生了报错。
我们顺着 Password
entity 的结构分析一下:
Password
领域实体内持有Lock
领域实体,而Lock
领域实体内持有HardwareInfo
领域实体。
在 getHardwareInfo()
方法中,存在查库查缓存行为。
当序列化框架实现序列化方法 toJSON
时,会通过反射调用JavaBean
中的属性方法,即各个get
方法。
凑巧的是,当时我们的测试环境redis
连接挂了,导致调用 getHardwareInfo()
直接抛错。
而在 JSON.toJSON
中,对异常进行了包装,丢失了一部分异常栈,因此在日志中看不到这块信息。
我是怎么发现redis连接挂掉了呢?
是因为QA同学反馈了其他的问题,其他的操作也无法操作redis
,在这些地方异常栈被打印了出来。
两类问题结合分析,得出了上面的正向、反向的通路。
而在线上环境我们可以直接通过监控报警观测到这种异常,不用兜这么大圈子。
小结
问题根因:测试环境的redis
连接挂了,导致领域对象的get
方法抛错,导致toJSON
直接失败。
解决:联系DBA
修复redis
。
教训:在使用了DDD架构的代码上,其实不建议对领域对象进行序列化操作,这种操作,无异于MVC架构中,对service进行序列化操作。做类似操作前,应该仔细评估+做好提前设计。