这篇文章回顾了一起由一行日志引发的线上P1事故,并深入分析了其技术原因、解决方案和最佳实践。
事故回顾:开发者在CountryDTO类中新增了一个isChinaName()方法,并在代码中通过FastJSON序列化该对象时,为了打日志触发了该方法。由于country字段此时为空,导致isChinaName()方法内调用equals时发生空指针异常,从而引发线上故障。
源码分析:文章通过源码追踪,揭示了FastJSON在序列化时会自动将符合条件的getXxx()和isXxx()方法当作属性的getter方法进行调用。关键在于com.alibaba.fastjson.util.TypeUtils#computeGetters方法,它会扫描类中以get或is开头的方法,并将其纳入序列化属性列表。
解决方案与代码规范:为避免此类问题,文章推荐了明确的代码规范:在不需要参与JSON序列化的getter/is方法上,显式地添加@JSONField(serialize = false)注解。这种方式一目了然,不依赖于开发人员对FastJSON内部规则的记忆,可以有效降低因团队知识差异导致的代码风险。
深层思考:文章最后进行了升华,指出了两个重要观点:
架构解耦:对特定的序列化框架(如FastJSON)的特性注解产生依赖,会提高系统耦合度,不利于未来替换框架。理想的设计应追求不依赖任何特定序列化框架的实现。
日志规范:强调了日志记录的必要性和克制性。不应随意、过量地打印日志,尤其要避免在像
getter这样的高频调用方法中打印日志,否则可能导致磁盘被快速写满等运维问题。
总的来说,这是一次从具体线上故障出发,深入技术原理,制定开发规范,并最终延伸到架构设计与开发哲学层面的完整复盘。