FastJson序列化驼峰-下划线转换问题踩坑记录

背景

问题描述

在MySQL数据表中,存在一个JSON结构的扩展字段,通过updateById进行更新写入操作。更新写入的同一个字段名出现了混合使用了驼峰命名和下划线命名两种格式。
ps: FastJson版本是1.2.83

问题影响

数仓同学离线统计数据时发现字段名有两种定义,产生了迷惑,不清楚如何统计;对线上实时链路还无任何影响。

原因定位

  • 首先例行公事,先检查多机房部署的代码版本是否一致,类似数据是否有持续写入
    • 检查过后发现,多区代码部署版本完全一致;
    • 且数据字段命名驼峰&下划线同时存在的数据持续有写入;
  • 检查下代码中是否有不同的写入位置,且写入的字段定义规则不一致
    • 发现这个字段只有一处更新写入,且通过业务日志看这个字段没有任何问题;
  • 尝试测试环境复现
    • 首先检查了下测试环境是否存在类似数据,发现并没有;
    • 其次在测试环境尝试构造请求写入,并未复现;
  • 到了常规猜测环节
    • 首相想到的是否有配置了全局的fastjson序列化驼峰下划线转换配置
      • 发现确实是有配置的,全局配置成了驼峰格式转换为下划线;配置如下:
@Configuration
public class FastJsonConfig {@Beanpublic SerializeConfig serializeConfig(){SerializeConfig serializeConfig = SerializeConfig.getGlobalInstance();serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;return serializeConfig;}
}
  • 那么新的问题来了,配置了全局转换为下划线的情况呢?
    首先在mybatis Xxx.xml 中的update语句里有
<if test="extraInfo != null">extra_info = #{extraInfo, typeHandler=com.xxx.config.typehandler.JsonTypeHandler},
</if>

result里面配置有如下转换:

<result column="extra_info" jdbcType="VARCHAR"javaType="com.xxx.dao.model.SoundtrackDO$SoundtrackExtraInfo"typeHandler="com.xxx.config.typehandler.JsonTypeHandler"property="extraInfo"/>

JsonTypeHandler 的代码如下:

public class JsonTypeHandler<T> extends BaseTypeHandler<T> {private Class<T> type;public JsonTypeHandler(Class<T> type) {this.type = type;}@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)throws SQLException {ps.setString(i, JSON.toJSONString(parameter));}@Overridepublic T getNullableResult(ResultSet rs, String columnName) throws SQLException {return parseObject(rs.getString(columnName));}@Overridepublic T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {return parseObject(rs.getString(columnIndex));}@Overridepublic T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {return parseObject(cs.getString(columnIndex));}private T parseObject(String result) {return JSON.parseObject(result, type);}
}

从配置代码上没有看出任何毛病,自测也是符合预期,一切都像是那么合理,为什么线上环境就出现了意外场景呢?依然很疑惑。
到了这里头上顶了三个问号,难道说这跟bean的初始化顺序有关?因为这个场景我们是消费rocketMq消息后调用mysql的更新语句,也就会执行到JsonTypeHandler这里的代码;而这里如果是消费者bean先初始化完成,代码开始消费,而此时序列化全局配置bean还没初始化完成是不是就用了默认的序列化规则呢,看起来很合理的猜测,所以我们开始了验证:

首先验证默认情况:
在这里插入图片描述
在这里插入图片描述
从图上看默认是驼峰,很符合预期;
那我们就尝试复现下先序列化,在设置全局参数的场景:
在这里插入图片描述
在这里插入图片描述
我们是先开启线程1序列化输出ss1,主线程睡眠两秒后,执行全局赋值驼峰下划线转换策略,再开启线程2,序列化输出ss2;从执行结果中可以看出,问题已经出现了,在我们设置驼峰转下划线格式后,ss2输出的依然是驼峰格式。
至此我们基本定位问题为:序列化策略propertyNamingStrategy参数赋值与序列化执行先后关系导致;从业务代码角度来说的话,就是RocketMq consumer bean初始化(因为初始化后,有消息就会立即消费,并不会等所有不强相关bean都初始化完成) 与 serializeConfig bean初始化先后顺序导致;测试环境因为消息太少所以没有命中这个问题。

接下来我们又提出个疑问,1.我们可以看到JSON.toJSONString方法是每次执行都会去获取SerializeConfig.globalInstance,那么如果每次都获取是不是服务启动完成后,就恢复了呢,但从数据库的数据上看并不是;
通过debug我们发现,fastjson 对一个对象首次序列化之后会将其存储再一个容器内com.alibaba.fastjson.serializer.SerializeConfig#serializers;后续在对这个对象序列化时是通过获取容器中存储的序列化规则进行处理的,并不是重新获取一遍配置,这也就是为啥服务启动完成后没有恢复的原因;也是fastjson的一个提升性能的设计。
对于为什么在线链路没有出现问题,这就是fastjson的一个机制了,反序列化时会先匹配字段名完全相同的字段值,没有的情况下还会匹配对应的驼峰或者下划线格式的字段名进行赋值,所以从接口返回上看不出问题。
好了,问题已经定位完成,后续就是如何解决了;

解决方案

1.提高serializeConfig bean的加载时机

例如在serializeConfig 配置处添加 @Order(1) 注解

但这个方式后续如果有人调整调整优先级可能会重新出现这个问题;
2.添加Consumer bean和 serializeConfig bean的依赖关系

例如在Consumer bean上配置@DependsOn("serializeConfig")

这样影响范围最小,切不会轻易受到其他调整的影响
3.抛弃serializeConfig全局配置,在使用场景进行单独处理,这个就是乱说了,这种场景完全不敢尝试,虽说看起来是短痛;不在有类似的问题出现,但过于痛,可能会踩大坑,不建议。

最终选择方案2

总结一下

1.这种全局配置要谨慎使用,且这种加载优先级设置还不是最高的,且不好被开发人员发现的配置话尽量少用吧,避免坑自己更避免坑别人
2.对于老服务,还是需要多观察下配置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/52744.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单链表——环形链表II

方法一 难想&#xff0c;但代码容易实现 根据第一道环形链表的题目我们可以得知快慢指针相交的节点&#xff0c;但是如果想要知道进入环形链表的第一个节点&#xff0c;我们就还需要定义一个指针从链表的头节点开始&#xff0c;与相交的节点同时行走&#xff0c;当两个节点重…

LeetCode刷题:3.无重复字符的最长子串

问题&#xff1a;首先分析问题得出需求 1.要求得到一个唯一最长子串的序列的长度。 子串&#xff1a;依据其形式是拥有一段长度的&#xff0c;所以考虑滑动窗口 唯一&#xff1a;考虑使用HashSet 需求描述&#xff1a;要求得到滑动窗口的大小&#xff0c;也就是左右指针的距离&…

milvus多个Querynode,资源消耗都打在一个节点上

milvus 查询时的原理 当读取数据时&#xff0c;MsgStream对象在以下场景中创建&#xff1a; 在 Milvus 中&#xff0c;数据必须先加载后才能读取。当代理收到数据加载请求时&#xff0c;会将请求发送给查询协调器&#xff0c;查询协调器决定如何将分片分配到不同的查询节点。…

NoSql数据库Redis集群

一、关系型数据库和 NoSQL 数据库 1.1 数据库主要分为两大类&#xff1a;关系型数据库与 NoSQL 数据库 关系型数据库 &#xff0c;是建立在关系模型基础上的数据库&#xff0c;其借助于集合代数等数学概念和方法来处理数据库中的数据主流的 MySQL 、 Oracle 、 MS SQL Server…

uniapp、微信小程序车牌的录入的解决方案

结合uv-ui进行编写&#xff0c;键盘使用uv-ui的组件&#xff0c;其他由我们自己编写。 <template><div class"addCarContent"><div class"boxContent"><div class"carCodeInput" click"getIndex"><div:cl…

紧急通知:避坑花生壳,花生壳退钱!!!推荐使用cpolar

有个需求&#xff0c;需要使用内网穿透功能。 本地使用花生壳搭建还算可以。 基于Ubantu。 然后再通过远程进行了搭建。 但是&#xff0c;搭建不成功。 一直报处于离线状态。 给花生壳客服反馈了&#xff0c;对方技术人员也无法解决。 协商退钱&#xff0c;不同意。 网上…

第八周:机器学习

目录 摘要 Abstract 一、注意力机制V.S.自注意力机制 1、引入 2、注意力机制 3、自注意力机制 二、自注意力机制 1、输入 2、输出 3、序列标注 4、Multi-head Self-attention 5、比较 总结 摘要 前两周学习了CNN的基本架构&#xff0c;针对全局信息的考虑问题&…

算法的学习笔记—连续子数组的最大和

&#x1f600;前言 在算法问题中&#xff0c;求解连续子数组的最大和是一个经典问题。给定一个整数数组&#xff0c;找到一个连续的子数组&#xff0c;使得其元素之和最大。本文将详细讲解如何解决这个问题&#xff0c;并提供Java实现代码。 &#x1f3e0;个人主页&#xff1a;…

SpringBoot对接Midjourney Api

提示&#xff1a;SpringBoot对接Midjourney Api 文章目录 目录 文章目录 后端代码 导包 controller层 工具类层 前端代码 申请API 测试结果 后端代码 导包 <!--添加hutool的依赖--><dependency><groupId>cn.hutool</groupId><artifactId&g…

黑神话悟空 PC端配置需求详解:如何为不同游戏体验选择合适的配置?

《黑神话&#xff1a;悟空》是一款备受期待的动作角色扮演游戏&#xff0c;由游戏科学&#xff08;Game Science&#xff09;开发&#xff0c;基于《西游记》改编。随着游戏的发布&#xff0c;许多玩家都在关心一件事&#xff1a;我的电脑能带动这款游戏吗&#xff1f;本文将详…

centos7 xtrabackup mysql(8)压缩 全量备份 还原(4)

centos7 xtrabackup mysql&#xff08;8&#xff09;压缩 全量备份 还原&#xff08;4&#xff09; 查看版本&#xff1a; xtrabackup --version qpress --help 主机端 mysql -u root -p 1234aA~1 use company_pro; insert into employee(name) value (‘20240823_1401’);…

MT3608L 2.5A,高效率1.2MHz电流模式升压转换器芯片IC

一般描述 MT3608L是一款恒频、6针SOT23电流模式升压转换器&#xff0c;适用于小型、低功率应用。MT3608L开关频率为1.2 MHz&#xff0c;允许使用高度小于2mm的微型、低成本电容器和电感器。内部软启动可产生小浪涌电流&#xff0c;延长电池寿命。 MT3608L具有在…

JavaWeb JavaScript ⑥ 事件

你摸黑偷偷赶得路&#xff0c;都会变成意外来袭时你少受的苦 —— 24.8.29 一、什么是事件 HTML 事件可以是浏览器行为&#xff0c;也可以是用户行为。 当一些行为发生时,可以自动触发对应的JS函数的运行,我们称之为事件发生&#xff0c;JS的事 件驱动指的就是行为触发代码运行…

ComfyUI SDXL Prompt Styler 简介

SDXL Prompt Styler 来自于 comfyui-art-venture 节点 style 已经更新 旧版本的 sai-line art 变更为 line art log_prompt 已经更新 旧版本的 false 变更为 Yes 或 No style_name 已经更新 旧版本的 true &#xff08;不再适用&#xff09;&#xff08;可以尝试对应style中…

【IoT】将各类遥控器(红外,频射,蓝牙,wifi,Zigbee)等设备接入米家,实现家庭物联网设备控制(以极米Z7X投影仪为例)

【IoT】将各类遥控器&#xff08;红外&#xff0c;频射&#xff0c;蓝牙&#xff0c;wifi&#xff0c;加密&#xff09;等设备接入米家&#xff0c;实现家庭物联网设备控制&#xff08;以极米Z7X投影仪为例&#xff09; 文章目录 1、三种主流遥控方式&#xff08;红外&#xff…

看新闻知补贴不用专门薅羊毛!让工作变舒服的5个黄金法则——早读(逆天打工人爬取热门微信文章解读)

你们都不看新闻吗&#xff1f; 引言Python 代码第一篇 洞见 让工作变舒服的5个黄金法则第二篇 故事之散户结尾 (发了3000亿以旧换新补贴&#xff0c;大家没有感觉到力度吗&#xff1f; 时间到今年年底&#xff0c;9月-12月是消费区&#xff0c;中间夹杂个双十一&#xff0c;现在…

一文搞懂 js 原型和原型链

文章目录 一、前言二、原型2.1 概念2.2 获取原型的方法2.2.1 __proto__获取方式2.2.2 通过构造函数prototype 属性获取2.2.2 ES6 class 通过Object.getPrototypeOf()获取类原型 2.3 通过原型实现继承2.4 原型的作用 三、 原型链四、ES6实现继承五、综述 一、前言 原型和原型链…

深入学习SQL优化的第五天(最后一天)

子查询 1321 餐 馆 营 业 额 变 化 增 长 1321. 餐馆营业额变化增长 表: Customer------------------------ | Column Name | Type | ------------------------ | customer_id | int | | name | varchar | | visited_on | date | | amount …

Linux网口指令

一 查看配置 ifconfig 二 修改IP sudo ifconfig ens33 192.168.150.100 netmask 255.255.255.0