一次奇妙的fegin排错旅行
起因
一个同事调用fegin的时候,发现调用返回的是null。但是日志却打印出了返回值。简单的来说,
@GetMapping("/contract/getSignerInfo")
public ContractSignerRecord getSignerInfo(@RequestParam String contractId);
调用了这个东西,然后在下面使用的时候,发现,出错了,包了空指针异常。然后逐步打印参数,定位到这个返回值是空的,本来以为是简单的熔断,但是奇异的事情发生了,我们配置的fegin日志的打印,打印出了返回,也就是说,明明有返回,没走熔断,但是却拿不到参数。本来有经验的人一眼就看出是序列化的问题,但是人菜,没想到什么序列化的坑。所以走了很多弯路。
看看我是怎么踩坑的。
首先排查熔断
是不是接口调用超时,是不是走了熔断,是不是接口发生了变化导致拿不到。首先发现这个接口的链路很短,而且很快就返回,基本排除超时,然后排查是不是接口发生了变化,对应了一遍没有(强烈建议把api抽离出公共模块,这样可以用编译来排除是不是接口发生了变化)。
然后想的是不是序列化错误
从日志里看到,是有返回的,但是对象却没有,从原理上来想,fegin走的是http请求,我能打印返回,说明我调用成功了,但是没有对象,是否是返回值序列化为对象失败,然后导致直接返回null。我想的是不是,这个dto对象是不是改变了,导致了反序列化失败,然后我重新打包部署了服务提供方和服务消费者,这样保证两个都是最新的dto。还是null。然后我把日志打印的值拿出来直接用gson反序列化对象,发现,可以反序列,这就陷入了死合同。
回想自己做了什么
这个接口以前都是好的,就在我整合了一下统一配置,发现就报错了,其他人都没问题,就这个接口,死也没有。那就没办法了,回滚吧。我就只能回滚配置,然后一个个的删除新增的配置,对比少的配置,一个个测试。发现是因为少了这个一个小小的配置。
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
这个明显是date字段序列化的指定,回头一看dto,里面果然有个date类型的字段。
总结
问题就这么找到了,其实有经验的一眼就能看出是序列化的原因,我却因为排查了其他的方向。
另外,提一句,序列化和反序列化的类,date类型请指定时区,不建议使用枚举类型(好像是不同的反序列化框架会存在不同)。