当前位置: 首页 > news >正文

记使用sjson的一次小事故

1. 前言

之前在设计一个兼容函数的时候,使用了sjson动态设入参数,从而实现一些参数的兼容。大致的逻辑如下所示:

// 有一堆不规则的json数据
{"a":"aaa","b":"bbb","any_key1":{"key":"value"},"any_key2":{"key":"value"}
}
// 因为any_key1和any_key2这样的数字字符串的key还会有新增,所以这个是无法固定的,没有办法给到一个具体的结构体解析,于是就人为的兼容,给它们在处理的时候包一个外层的key
{"a":"aaa","b":"bbb","number_key_data":{"any_key1":{"key":"value"},"any_key2":{"key":"value"}}
}
// 这样处理之后,我只要定义number_key_data,我就可以获取到具体的字符串数字为key的数据

2. 实现

采用sjson对对应的非特定key进行新的结构值设入,然后做一个兼容即可实现上面的逻辑,于是有这段代码出来了。

package gjson_set_studyimport ("encoding/json""fmt""github.com/tidwall/sjson""testing"
)func TestGjsonSet(t *testing.T) {data := `{"a":"aaa","b":"bbb","any_key1":{"key":"value"},"any_key2":{"key":"value"}}`dataMap := map[string]interface{}{}err := json.Unmarshal([]byte(data), &dataMap)if err != nil {panic(err)}fmt.Println(adaptNumberKey(data, dataMap))// output// {"a":"aaa","b":"bbb","number_key_data":{"any_key1":{"key":"value"},"any_key2":{"key":"value"}}} <nil>
}type Data struct {A             string `json:"a"`B             string `json:"b"`NumberKeyData map[string]struct {Key   string `json:"key"`Value string `json:"value"`} `json:"number_key_data"`
}var specificKeys = map[string]bool{"a": true,"b": true,
}func adaptNumberKey(data string, dataMap map[string]interface{}) (string, error) {var err errorfor k, v := range dataMap {if _, ok := specificKeys[k]; ok {continue}data, err = sjson.Delete(data, k) // remove firstif err != nil {return "", fmt.Errorf("delete key data error, key=%s, err=%w", k, err)}data, err = sjson.Set(data, "number_key_data."+k, v)if err != nil {// log...return "", fmt.Errorf("set number_key_data error, key=%s, err=%w", k, err)}}return data, nil
}

上面的实现,不出意外的话是不会有任何的问题,但是不出意外的意外出现了,当类似的逻辑代码上线之后,我们发现一个问题:容器会爆内存。这代码也实现了自测,也过了QA的测试,为什么会突然爆内存呢?

3. 问题

容器爆内存的问题出现了,但并不是所有的数据都爆内存,有一组数据100%会爆内存,它们的数据类似:

{"a":"aaa","b":"bbb","1000000":{"key":"value"},"50000000":{"key":"value"}}

比较明显的是出现了数字字符串的key,然后结合代码看了一下,刚开始也没看出啥异常,觉得这个修改后的数据应该是:

{"a":"aaa","b":"bbb","number_key_data":{"1000000":{"key":"value"},"50000000":{"key":"value"}}}

但后面发现爆内存的问题,又想起了设置数组的方式,当前的代码逻辑如果遇到数字key,就会被认为是在设置数组,开辟几百万甚至上千万长度的数组?(细思极恐) 于是就发现了爆内存的问题所在:数字key在未经特殊标识的情况下,会被认定为数组,于是这个设置key的过程,就变成了对一个key的长为1000000的数组设置值(后者是50000000),可怕

4. 解决方法

于是参看源码,照着sjson的set方法一路向下看,可以发现如果在parsePath 中我们对路径添加了: 的前缀,sjson会强制把这个key当做string key,而在atoui中不会将其解析为一个具体的数字,进而导致对字符串key的设置,变成对数组的设值。

func parsePath(path string) (res pathResult, simple bool) {var r pathResultif len(path) > 0 && path[0] == ':' { // 如果含有:符号,这个key会被强制认定为keyr.force = truepath = path[1:]}for i := 0; i < len(path); i++ { // 对path进行分解if path[i] == '.' {r.part = path[:i]r.gpart = path[:i]r.path = path[i+1:]r.more = truereturn r, true}if !isSimpleChar(path[i]) {return r, false}if path[i] == '\\' {// go into escape mode. this is a slower path that// strips off the escape character from the part.// ...}return r, true
}// atoui does a rip conversion of string -> unigned int.
func atoui(r pathResult) (n int, ok bool) {if r.force {return 0, false}for i := 0; i < len(r.part); i++ {if r.part[i] < '0' || r.part[i] > '9' {return 0, false}n = n*10 + int(r.part[i]-'0')}return n, true
}

于是修改代码逻辑,将所有key的前缀都加上:的标识。

func adaptNumberKey(data string, dataMap map[string]interface{}) (string, error) {var err errorfor k, v := range dataMap {if _, ok := specificKeys[k]; ok {continue}data, err = sjson.Delete(data, k) // remove firstif err != nil {return "", fmt.Errorf("delete key data error, key=%s, err=%w", k, err)}data, err = sjson.Set(data, "number_key_data."+":"+k, v)if err != nil {// log...return "", fmt.Errorf("set number_key_data error, key=%s, err=%w", k, err)}}return data, nil
}
// Output: {"a":"aaa","b":"bbb","number_key_data":{"1000000":{"key":"value"},"50000000":{"key":"value"}}} <nil>

5. 小结

忽然想到遇到的这个小问题,当时就觉得还是自己单测的场景不够全面,导致了这次爆内存的问题发生,还好有临时解决方案,不然对线上服务造成的影响还真不小。通过这个事例,再一次告诫自己在后续的代码编写中,对于通用功能的逻辑代码,要尽可能的思考一些边缘case,从而避免在上线后边缘case导致代码崩溃的现象出现。

相关文章:

记使用sjson的一次小事故

1. 前言 之前在设计一个兼容函数的时候&#xff0c;使用了sjson动态设入参数&#xff0c;从而实现一些参数的兼容。大致的逻辑如下所示&#xff1a; // 有一堆不规则的json数据 {"a":"aaa","b":"bbb","any_key1":{"k…...

如何在iOS系统抓取log

前言&#xff1a;因为作者目前工作领域和苹果智能家居有关&#xff0c;然后发现一些bug其实是apple sdk原生code的问题&#xff0c;所以需要给apple提radar单&#xff0c;就需要抓ios端Log充当证据给apple看&#xff0c;其实ios抓log非常简单&#xff0c;大家感兴趣可以学习下哦…...

【嵌入式——QT】Charts常见的图表的绘制

【嵌入式——QT】Charts常见的图表的绘制 柱状图QBarSetQBarSeriesQBarCategoryAxis图示 饼图堆叠柱状图百分比柱状图散点图和光滑曲线图代码示例 柱状图 QBarSet 用于创建柱状图的数据集。 主要函数 setLabel()&#xff1a;设置数据集标签 &#xff1b;setLabelBrush()&am…...

pandas读写excel,csv

1.读excel 1.to_dict() 函数基本语法 DataFrame.to_dict (self, orientdict , into ) --- 官方文档 函数种只需要填写一个参数&#xff1a;orient 即可 &#xff0c;但对于写入orient的不同&#xff0c;字典的构造方式也不同&#xff0c;官网一共给出了6种&#xff0c…...

清华大学突破性研究:GVGEN技术,7秒内从文字到3D高保真生成

引言&#xff1a;3D模型生成的挑战与机遇 随着计算机图形学的发展&#xff0c;3D模型的生成在各个行业中变得越来越重要&#xff0c;包括视频游戏设计、电影制作以及AR/VR技术等。在3D建模的不同方面中&#xff0c;从文本描述生成3D模型成为一个特别有趣的研究领域&#xff0c;…...

软件测试要学习的基础知识——黑盒测试

概述 黑盒测试也叫功能测试&#xff0c;通过测试来检测每个功能是否都能正常使用。在测试中&#xff0c;把程序看作是一个不能打开的黑盒子&#xff0c;在完全不考虑程序内部结构和内部特性的情况下&#xff0c;对程序接口进行测试&#xff0c;只检查程序功能是否按照需求规格…...

如何用Airtest脚本连接无线Android设备?

之前我们已经详细介绍过如何用AirtestIDE无线连接Android设备&#xff0c;它的关键点在于&#xff0c;需要先 adb connect 一次&#xff0c;才能点击 connect 按钮无线连接上该设备&#xff1a; 但是有很多同学&#xff0c;在使用纯Airtest脚本的形式连接无线设备时&#xff0c…...

c语言函数大全(C开头)

c语言函数大全(C开头) There is no nutrition in the blog content. After reading it, you will not only suffer from malnutrition, but also impotence. The blog content is all parallel goods. Those who are worried about being cheated should leave quickly. 函数名…...

初始Redis关联和非关联

基础篇Redis 3.初始Redis 3.1.2.关联和非关联 传统数据库的表与表之间往往存在关联&#xff0c;例如外键&#xff1a; 而非关系型数据库不存在关联关系&#xff0c;要维护关系要么靠代码中的业务逻辑&#xff0c;要么靠数据之间的耦合&#xff1a; {id: 1,name: "张三…...

Redis 更新开源许可证 - 不再支持云供应商提供商业化的 Redis

原文&#xff1a;Rowan Trollope - 2024.03.20 未来的 Redis 版本将继续在 RSALv2 和 SSPLv1 双许可证下提供源代码的免费和宽松使用&#xff1b;这些版本将整合先前仅在 Redis Stack 中可用的高级数据类型和处理引擎。 从今天开始&#xff0c;所有未来的 Redis 版本都将以开…...

生产者Producer往BufferQueue中写数据的过程

In normal operation, the producer calls dequeueBuffer() to get an empty buffer, fills it with data, then calls queueBuffer() to make it available to the consumer 代码如下&#xff1a; // XXX: Tests that fork a process to hold the BufferQueue must run bef…...

使用 Vite 和 Bun 构建前端

虽然 Vite 目前可以与 Bun 配合使用&#xff0c;但它尚未进行大量优化&#xff0c;也未调整以使用 Bun 的打包器、模块解析器或转译器。 Vite 可以与 Bun 完美兼容。从 Vite 的模板开始使用吧。 bun create vite my-app ✔ Select a framework: › React ✔ Select a variant:…...

如何设置IDEA远程连接服务器开发环境并结合cpolar实现ssh远程开发

文章目录 1. 检查Linux SSH服务2. 本地连接测试3. Linux 安装Cpolar4. 创建远程连接公网地址5. 公网远程连接测试6. 固定连接公网地址7. 固定地址连接测试 本文主要介绍如何在IDEA中设置远程连接服务器开发环境&#xff0c;并结合Cpolar内网穿透工具实现无公网远程连接&#xf…...

【项目管理后台】Vue3+Ts+Sass实战框架搭建二

Vue3TsSass搭建 git cz的配置mock 数据配置viteMockServe 建立mock/user.ts文件夹测试一下mock是否配置成功 axios二次封装解决env报错问题&#xff0c;ImportMeta”上不存在属性“env” 统一管理相关接口新建api/index.js 路由的配置建立router/index.ts将路由进行集中封装&am…...

制作一个RISC-V的操作系统六-bootstrap program(risv 引导程序)

文章目录 硬件基本概念qemu-virt地址映射系统引导CSR![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/86461c434e7f4b1b982afba7fad0256c.png)machine模式下的csr对应的csr指令csrrwcsrrs mhartid引导程序做的事情判断当前hart是不是第一个hart初始化栈跳转到c语言的…...

haproxy和keepalived的区别与联系

HAProxy&#xff08;High Availability Proxy&#xff09; 是一个开源的、高效且可靠的解决方案&#xff0c;主要用于负载均衡。它工作在应用层&#xff08;第七层&#xff09;&#xff0c;支持多种协议&#xff0c;如HTTP、HTTPS、FTP等。HAProxy通过健康检查机制持续监控后…...

云效 AppStack + 阿里云 MSE 实现应用服务全链路灰度

作者&#xff1a;周静、吴宇奇、泮圣伟 在应用开发测试验证通过后、进行生产发布前&#xff0c;为了降低新版本发布带来的风险&#xff0c;期望能够先部署到灰度环境&#xff0c;用小部分业务流量进行全链路灰度验证&#xff0c;验证通过后再全量发布生产。本文主要介绍如何通…...

pta L1-004 计算摄氏温度

L1-004 计算摄氏温度 分数 5 全屏浏览 切换布局 作者 陈建海 单位 浙江大学 给定一个华氏温度F&#xff0c;本题要求编写程序&#xff0c;计算对应的摄氏温度C。计算公式&#xff1a;C5(F−32)/9。题目保证输入与输出均在整型范围内。 输入格式: 输入在一行中给出一个华氏…...

毕业论文降重(gpt+完美降重指令),sci论文降重gpt指令——超级好用,重复率低于4%

1. 降重方法&#xff1a;gpt降重指令 2. gpt网站 https://yiyan.baidu.com/ https://chat.openai.com/ 3. 降重指令——非常好用&#xff01;&#xff01;sci论文&#xff0c;本硕大论文都可使用&#xff01; 请帮我把下面句子重新组织&#xff0c;通过调整句子逻辑&#xff0…...

Qt 多元素控件

Qt开发 多元素控件 Qt 中提供的多元素控件有: QListWidgetQListViewQTableWidgetQTableViewQTreeWidgetQTreeView xxWidget 和 xxView 之间的区别 以 QTableWidget 和 QTableView 为例. QTableView 是基于 MVC 设计的控件. QTableView 自身不持有数据. 使用QTableView 的 …...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...

raid存储技术

1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划&#xff0c;涵盖存储系统的布局、数据存储策略等&#xff0c;它明确数据如何存储、管理与访问&#xff0c;为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...

Qwen系列之Qwen3解读:最强开源模型的细节拆解

文章目录 1.1分钟快览2.模型架构2.1.Dense模型2.2.MoE模型 3.预训练阶段3.1.数据3.2.训练3.3.评估 4.后训练阶段S1: 长链思维冷启动S2: 推理强化学习S3: 思考模式融合S4: 通用强化学习 5.全家桶中的小模型训练评估评估数据集评估细节评估效果弱智评估和民间Arena 分析展望 如果…...

uniapp获取当前位置和经纬度信息

1.1. 获取当前位置和经纬度信息&#xff08;需要配置高的SDK&#xff09; 调用uni-app官方API中的uni.chooseLocation()&#xff0c;即打开地图选择位置。 <button click"getAddress">获取定位</button> const getAddress () > {uni.chooseLocatio…...