今天同事过来找我,给我发了一个生产故障如下:
`08-May-2020 18:10:53.862 SEVERE [http-nio-8080-exec-327] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [springmvc] in context with path [] threw exception [Request processing failed; nested exception is net.sf.json.JSONException: Error while setting property=effectdate type class java.lang.String] with root cause
net.sf.ezmorph.MorphException: Unable to parse the date 2020-04-21 00:00:00
at net.sf.ezmorph.object.DateMorpher.morph(DateMorpher.java:211)
at net.sf.ezmorph.MorpherRegistry.morph(MorpherRegistry.java:167)
at net.sf.json.JSONObject.morphPropertyValue(JSONObject.java:1381)
at net.sf.json.JSONObject.toBean(JSONObject.java:363)
at net.sf.json.JSONObject.toBean(JSONObject.java:233)`
说很奇怪,灰度和测试环境一直没出现这个报错,生产上有。带着他的疑问,我开始了故障排查。
net.sf.ezmorph.MorphException: Unable to parse the date 2020-04-21 00:00:00
这个报错,是因为无法解析这个日期格式导致的,解决方案如下:
//注册一个DateMorpher类,来处理对应的日期格式
MorpherRegistry mr = JSONUtils.getMorpherRegistry();
DateMorpher dm = new DateMorpher(new String[] { "YYYY_MM_DD",
"YYYY_MM_DD_HH_MM_ss", "HH_MM_ss", "YYYYMMDD",
"YYYYMMDDHHMMSS", "HHMMss", "yyyy-MM-dd HH:mm:ss" });
mr.registerMorpher(dm);
但是我们不是需要解决这么bug,而是我们要定位,为什么生产会有bug,而测试和灰度环境没有。
好了,现在我们开始描述下,我的分析方法。
首先我们对比下生产和测试代码执行的监控,我这边给大家推荐一款神器:arthas
通过arthas来监控报错代码运行状态:
我们去到生产环境,报错代码方法是morph方法:
net.sf.ezmorph.object.DateMorpher.morph(DateMorpher.java:211)
我们利用
trace net.sf.ezmorph.object.DateMorpher morph
提示No class or method is affected,表示DateMorpher这个类是通过ClassLoader加载的。
我们看下源码,可以得知,这个DateMorpher,是通过ClassLoader加载,导致通过arthas无法得到增强。
下一步,我们可以通过watch命令,看下net.sf.ezmorph.MorpherRegistry.morph 方法return的值是什么
生产环境:
net.sf.ezmorph.MorpherRegistry morph "{returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 105 ms.
ts=2020-05-17 14:22:23; [cost=0.355289ms] result=@ArrayList[
@Date[2020-05-17 14:22:23,551],
]
测试和演练环境:
net.sf.ezmorph.MorpherRegistry morph "{returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 105 ms.
ts=2020-05-17 14:22:23; [cost=0.039714ms] result=@ArrayList[
@String[0],
]
咱们可以发现,测试环境并没有初始化DateMorpher这个对象,测试环境默认使用的是BeanMorpher这个对象,来进行数据的转换,而BeanMorpher对任意日期格式处理,是不会产生报错。
再回到生产环境的DateMorpher这个源码中,发现报错就是由于我们没有设置日期格式导致。
总结:测试环境没有初始化DateMorpher对象,来对响应的日期进行处理,导致测试环境不会报错。而生产加载了DateMorpher对象。
而并没有定义yyyy-MM-dd HH:mm:ss格式日期,导致最终报错。而这个类是静态块,只有类被调用时候才会执行,生产上由于用户执行某个功能导致这个静态快被执行,而测试环境代码覆盖了部分功能,不会执行这个静态块。