序列化引发的血案

问题背景

早上来上班,突然值班群里有人报问题,说店铺属性在JD App前端显示错乱了。打开App,点开一个店铺查看,问题复现。打开内部店铺信息查询系统,发现查出来的属性确实有一部分错乱了。OMG~线上bug来了。
查看JONE系统,发现昨晚有人加班上线了新功能,甭问,肯定是这次上线造成的问题。尝试回滚一台线上机器,问题解决。来吧,全量回滚吧!悲催的绩效啊~

问题排查

1.店铺信息查询逻辑

App中的店铺信息是调用微服务shop-base的JSF(类似Dubbo)接口查询店铺基础信息的。这次问题也就是出在这个shop-base上。这个接口的入参中,传入的是一个需要查询的属性List,这些属性被定义成VenderAttributeEnum,也就是List< VenderAttributeEnum >。调用方把需要查询的属性指定好,塞入list传进接口。
下面是接口定义(省去无关的参数):

    public Result getShopAttributesByShopId(long shopId, List QueryShopInfoResult, etc...);

2.问题原因

询问了昨晚的开发者,新功能迭代中,增加了两个新属性。打开git日志,发现VenderAttributeEnum有修改,立刻查看修改记录,发现新增了两个属性。问题就在这里。VenderAttributeEnum的定义类似如下,新增了两个属性:

public enum VenderAttributeEnum {
    /*店铺Logo*/
    VENDER_SHOP_LOGOXXX, 
    /*店铺Id*/
    VENDER_SHOP_ID, 
    /*店名*/
    VENDER_SHOP_NAME, 
    /*店铺状态*/
    VENDER_SHOP_STATUS,
    /*新增属性1*/
    VENDER_NEW_ATT1,    -----------> 新增1
    /*新增属性2*/
    VENDER_NEW_ATT2,    -----------> 新增2
    /*Logo图片URI地址*/
    VENDER_SHOP_LOGO_XXX,      -----------> 开始错乱
    ......(省略几万个属性)

看上去这样修改没啥问题,但是这两个属性被加在了中间,而不是所有属性的末尾。经过确认,发现出现错乱的是从属性VENDER_SHOP_LOGO_XXX开始的,进一步验证了此处修改造成了错乱。

为什么加在了Enum中间就会出现错乱呢?

刚进公司的时候,自己学习过JSF的文档。其中提到过对于Bean和Enum的定义,属性顺序不能变化。因为JSF默认使用Msgpack做序列化。其特点就是序列化后字节数变小,而变小的原因数据压缩,其追求的是更小、更快。为了更小,MsgPack序列化的结果中只包含了value,而不包含key。因此,在进行反序列化的时候需要保证类中属性的顺序必须保证跟序列化时完全一致

  • Msgpack的介绍很多,这里推荐一个:
    https://www.cnblogs.com/sunzhenchao/p/8448929.html

3.解决方法

  • Msgpack是个坑,考虑使用hessian、protobuf,不再对属性的顺序有要求。也许后面还有人犯同样的错误。到底使用哪个,性能比较后决定。
  • 接口测试的时候要关注所有属性的值,而不是只测试新增的属性。自动化回归测试是个好方法。
  • 暴露新员工培训不足,对使用到的中台服务逐一培训学习。

教训是深刻的,千万不要再犯!

你可能感兴趣的:(序列化引发的血案)