当前位置: 首页 > 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 的 …...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...