前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >本地缓存不一致-记Jackson的MapSerializer序列化

本地缓存不一致-记Jackson的MapSerializer序列化

作者头像
江湖前辈黄药师
发布2021-04-19 16:06:46
9960
发布2021-04-19 16:06:46
举报
文章被收录于专栏:博客园迁移

踩坑-现象

现象: 线上出现运费险查询接口返回结果不符合预期 且短时间内同样的请求参数返回结果不一致

同样的参数 短时间内返回结果不一致 对应api的业务逻辑没有改变

根本原因:

seller-center应用使用的redisson用的jackson序列化 jackson序列化map时有坑

当map中的key是Integer类型对应的序列化执行器StdKeySerializers序列化后以string存储

value是integer类型对应的序列化执行器NumberSerializers序列化后还是integer

jackson的map序列化核心逻辑

com.fasterxml.jackson.databind.ser.std.MapSerializer#serializeTypedFields

代码语言:javascript
复制
    public void serializeTypedFields(Map<?,?> value, JsonGenerator gen, SerializerProvider provider,
            Object suppressableValue) // since 2.5
        throws IOException
    {
        final Set<String> ignored = _ignoredEntries;
        final boolean checkEmpty = (MARKER_FOR_EMPTY == suppressableValue);
		//循环map 并序列化key  和value
        for (Map.Entry<?,?> entry : value.entrySet()) {
            Object keyElem = entry.getKey();
            JsonSerializer<Object> keySerializer;
            if (keyElem == null) {
                keySerializer = provider.findNullKeySerializer(_keyType, _property);
            } else {
                if (ignored != null && ignored.contains(keyElem)) continue;
                //key是integer的1 !=null  取的默认StdKeySerializers序列化执行器
                keySerializer = _keySerializer;
            }
            final Object valueElem = entry.getValue();
    
            JsonSerializer<Object> valueSer;
            if (valueElem == null) {
                if (_suppressNulls) { // all suppression include null suppression
                    continue;
                }
                valueSer = provider.getDefaultNullValueSerializer();
            } else {
                valueSer = _valueSerializer;
                if (valueSer == null) {
                    //map的第一个entry进来 根据entry的vaule类型匹配序列化执行器
                    //这里value类型integer  --> NumberSerializers
                    valueSer = _findSerializer(provider, valueElem);
                }
                if (checkEmpty) {
                    if (valueSer.isEmpty(provider, valueElem)) {
                        continue;
                    }
                } else if (suppressableValue != null) {
                    if (suppressableValue.equals(valueElem)) {
                        continue;
                    }
                }
            }
            keySerializer.serialize(keyElem, gen, provider);
            try {
                valueSer.serializeWithType(valueElem, gen, provider, _valueTypeSerializer);
            } catch (Exception e) {
                wrapAndThrow(provider, e, value, String.valueOf(keyElem));
            }
        }
    }

map的 key 和value 分别匹配了不同的序列化执行器

map的key !=null 匹配到的是是 StdKeySerializers来序列化

map的value 根据value的类型来选择序列化 interger 匹配到了NumberSerializers序列化

重点来了 获取了map key 和 value的序列化执行器 下面开始序列化

两种序列化的具体实现

StdKeySerializers integer -> number -> string

NumberSerializers integer 还是integer

---------------------------------- 分隔线 -------------------------------------

艰难出坑-问题排查:

查询运费险接口实现加了本地缓存

缓存结构 本地缓存 -> redis -> DB 一层一层从上往下捞数据, 捞到数据再从下往上设置数据

该接口的返回值有 布尔值 且变量名is开头

/**

* 是否支持运费险

*/

private Boolean isSupport;

怀疑是lombok的 @Data注解自动生成的get set 有问题

故而加上手动生成的get set 重发应用 结果依旧是返回结果不一致

查看对应的类的class文件 发现isSupport这个变量的get set 方法和手动生成的是一致的 不是因为该变量命名Date问题引起,但是此类命名应该要避免

进一步推断 是代码问题引起 加上日志重发

发现是获取商家运费险的开通状态isOpen 导致的问题 获取商家开通状态代码

加上日志重发

问题就是这个map的containKey 导致的, 修改代码发布解决问题

出坑记录

从redis获取的map参数类型变了 put 到redis的map 和redis去除的map数据结构不一致

查询redis对应的key的值

具体redis存储map 序列化的过程

这里发现是redisson用的是Jackson序列化

重点: 遍历map的key vlue 获取对应class类型的序列化执行器 然后执行序列化

map的 key 和value 分别匹配了不同的序列化执行器

map的key !=null 匹配到的是是 StdKeySerializers来序列化

map的value 根据value的类型来选择序列化 interger 匹配到了NumberSerializers序列化

重点来了 获取了map key 和 value的序列化执行器 下面开始序列化

两种序列化的具体实现

StdKeySerializers integer -> number -> string

NumberSerializers integer 还是integer

map<integer, integer> 被jackson序列化后成了 {"@class":"java.util.HashMap","1":0,"2":0}

规范

如非必要不要把map用与缓存

如非必要不要把map用与dubbo接口的返回中

DTO中的布尔值 不要用is开头来命名 避免不同序列化导致的问题

action

梳理上述规范涉及存量代码 &评估改造风险

问题?

json 和 gson 对于map的序列化是怎么实现的呢?

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-04-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 踩坑-现象
  • 根本原因:
  • 艰难出坑-问题排查:
  • 出坑记录
  • 规范
  • action
  • 问题?
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档