一桩关于Json序列化引发的惨案(Go系统)

文章目录

  • 前言
  • 突然崩了
  • 排查问题
  • 关于go的json库
    • 什么是反射
  • 解决大结构体序列化的性能问题
    • 干掉大结构体
    • 减少反射使用
      • 一些好用的第三方序列化包
    • 自定义序列化
  • 写在最后

前言

一个风和日丽的下午,线上系统突然开始报警(系统温馨提示,您的服务接口响应耗时已突破1s )
(-_-|||)
在这里插入图片描述
突破天际的耗时线(并且伴随着实例OOM重启问题)!!!

当时脑门汗蹭的一下的就冒出来了,趁老板没杀过来之前立马开始紧急排查处理~

突然崩了

由于最近没有发布,这波服务崩的莫名其妙,为了不影响线上业务,观察服务异常情况和资源监控后,首先对每个实例的资源配置进行了提升

重新发布之后,系统算是稳定下来了,然而接口耗时仍然比出问题之前要高了接近2倍。

排查问题

针对服务性能异常的排查,笔者在之前的文章《线上GO服务出现GC故障,我当时就急了》 中已经提到过,这里就不再赘述。

通过pprof采样线上cpu耗时之后,锁定了有问题的代码片段
在这里插入图片描述在这里插入图片描述
问题已经很明显了,由于上报结果集中需要序列化的结构体太大,占用了过多的CPU和内存资源,导致线上服务性能急剧下降,并且产生了OOM问题。

问题很快被修复了,但是也引发了笔者对于json序列化性能的思考:抛开常规的优化手段不谈,单从json序列化本身是否还有优化空间呢?

关于go的json库

这里不得不先向不了解的朋友们简单介绍一下go的标准json库 : )

Go 语言的标准 json 库是一个 encoding/json 包,它提供了一系列的 API 函数,用于从 Go 对象生成 json 文档,以及从 json 文档中填充 Go 对象。它的序列化和反序列化主要通过包中的 json.Marshal()json.Unmarshal() 方法实现。

Go 语言中的 json 序列化过程不需要被序列化的对象预先实现任何接口,它会通过反射获取结构体或者数组中的值并以树形的结构递归地进行编码。

那么问题来了,首先需要明确的是,go中反射操作的性能是比较差的,而标准库恰好又大量使用反射获取值,较为耗费 CPU 配置;其次频繁分配对象,也会带来内存分配和 GC 的开销;

什么是反射

前面提到了go语言的反射,反射是什么,为什么反射操作会带来性能问题,这里给出一些简单的解释;

Go 的反射是指在运行时动态地获取和操作对象的类型和值的能力,它主要通过 reflect 包中的 TypeValue 类型来实现。Go 的反射比较耗性能的原因主要有以下几点:

  1. 反射操作需要调用一系列的函数,而不是直接访问内存地址,这会增加函数调用的开销和栈空间的占用。
  2. 反射操作需要使用类型断言和类型转换,这会增加运行时的类型检查和内存分配的开销。
  3. 反射操作需要使用反射包中的方法,而不是编译器优化过的内置方法,这会降低执行效率和缓存命中率。
  4. 反射操作需要使用递归和状态机等技术,这会增加逻辑复杂度和计算量。

根据一些性能测试,Go 的反射操作可能比正常操作慢几十倍甚至几百倍。因此,在不必要的情况下,应该尽量避免使用反射,或者使用一些高性能的反射库来优化反射性能。

解决大结构体序列化的性能问题

干掉大结构体

是的没错,如果解决不了问题,那么我们考虑解决引发问题的对象~(听着怪怪的)

思考一下为什么业务中会出现需要序列化大结构体的场景,是不是所有字段都是被需要的?服务中我们提倡按需加载,对于序列化也是一样的。

我们可以根据需要定一个专门用于序列化的结构体,该结构体中只包含我们需要的字段;

对于可迭代的数据结构来说,我们还可以进行分批处理,减少单次序列化的压力;

减少反射使用

如何这样还不够,又或者被序列化对象确实很大,那么我们可以考虑不使用标准json库,替换成其他底层实现中反射使用少或者完全不使用反射的第三方json库;

一些好用的第三方序列化包

关于第三方高性能json库的详细评测和使用建议,可以参考这篇文章:《探究|Go JSON 三方包哪家强?》

自定义序列化

如果不考虑泛用性,追求极致的性能的情况下,我们还可以祭出终极杀招,自定义序列化协议

由于使用场景很小,这里简单抛砖引玉给出一个例子,我们可以基于go语言 encoding/binary 库对结构体进行轻量级序列化和反序列化。这种方式的优点是简单高效,缺点嘛也很明显,有一定的开发和维护成本

关于encoding/binary的更多使用姿势本篇就不多赘述了,下面上一个简单的序列化例子,小伙伴们自行感受下

package mainimport ("bytes""compress/gzip""encoding/base64""encoding/binary""fmt""io"
)type User struct {Name     stringAge      int64IsSingle bool
}type Encoder struct {w io.Writer
}func NewEncoder(w io.Writer) *Encoder {return &Encoder{w: w}
}func (e *Encoder) encode(user *User) {_ = binary.Write(e.w, binary.LittleEndian, uint16(len(user.Name))) // 非固定长度数据需要定义读取长度_ = binary.Write(e.w, binary.LittleEndian, []byte(user.Name))_ = binary.Write(e.w, binary.BigEndian, user.Age)_ = binary.Write(e.w, binary.LittleEndian, user.IsSingle)
}type Decoder struct {r io.Reader
}func NewDecoder(r io.Reader) *Decoder {return &Decoder{r: r}
}func (d Decoder) decode(user *User) {var namelen uint16_ = binary.Read(d.r, binary.LittleEndian, &namelen)nameBytes := make([]byte, namelen)_ = binary.Read(d.r, binary.LittleEndian, &nameBytes)user.Name = string(nameBytes)var age int64_ = binary.Read(d.r, binary.BigEndian, &age)user.Age = agevar single bool_ = binary.Read(d.r, binary.LittleEndian, &single)user.IsSingle = single
}func main() {user1 := &User{Name:     "小A",Age:      20,IsSingle: true,}var buf bytes.Bufferbase64Writer := base64.NewEncoder(base64.StdEncoding, &buf)compressor := gzip.NewWriter(base64Writer)encoder := NewEncoder(compressor)encoder.encode(user1) // 编码_ = compressor.Close()bin := buf.Bytes()base64Reader := base64.NewDecoder(base64.StdEncoding, bytes.NewReader(bin))decompressor, _ := gzip.NewReader(base64Reader)decoder := NewDecoder(decompressor)var newUser1 Userdecoder.decode(&newUser1) // 解码fmt.Println(newUser1)
}

写在最后

日常业务流程中,我们不可避免的需要与各种各样的序列化场景打交道(最常见的,日志上报),在使用序列化的过程中,我们需要时刻做到对数据量的把控,做到心中有数。避免出现因序列化缓影响线上正常业务请求。

问题解决,可以安心睡一觉了~~

在这里插入图片描述

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

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

相关文章

MongoDB常用操作

一、MongoDB常用操作(一) 1、INSERT > db.User.save({name:zhangsan,age:21,sex:true}) > db.User.find() _id组合 Objectld是、id”的默认类型。Objectld使用12字节的存储空间,每个字节二位十六进制数字,是一个24位的字…

CCF-CSP真题《202303-3 LDAP》思路+python,c++满分题解

想查看其他题的真题及题解的同学可以前往查看:CCF-CSP真题附题解大全 试题编号:202303-3试题名称:LDAP时间限制:12.0s内存限制:1.0GB问题描述: 题目背景 西西艾弗岛运营公司是一家负责维护和运营岛上基础设…

实验:验证TCP套接字传输的数据不存在数据边界

来源&#xff1a;《TCP/IP网络编程》 学习ing 自己动手&#xff0c;把坑踩一遍&#xff0c;也可以学习到很多。 Linux环境下: 客户端&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <…

8.4.2 【Linux】XFS 文件系统还原 xfsrestore

xfsdump 的复原使用的是 xfsrestore 这个指令。 用 xfsrestore 观察 xfsdump 后的备份数据内容 要找出 xfsdump 的内容就使用 xfsrestore -I 来查阅即可&#xff01;不需要加任何参数&#xff01;因为 xfsdump与 xfsrestore 都会到 /var/lib/xfsdump/inventory/ 里面去捞数据来…

C/C++开发,opencv基于FileStorage读写文件介绍及示例

目录 一、FileStorage类 1.1 FileStorage类说明 1.2 FileStorage类写入说明 1.3 FileStorage类读取说明 二、FileStorage类应用示例 2.1 应用代码 2.2 工程组织&#xff08;Makefile&#xff09; 2.3 编译及测试 一、FileStorage类 1.1 FileStorage类说明 FileStorage类在ope…

Linux搭建Discuz论坛

环境&#xff1a;redhat 9 mysql 8 Discuz 3.5 题目要求&#xff1a;在 bbs.example.com 主机上创建 Discuz 论坛&#xff0c;数据库服务器使用 db.example.com 主机的 bbs 数据库实例&#xff0c;该实例由 MySQL数据库软件提供服务。 题目要求没有说是在一台虚拟机…

jenkins使用ftp工具,上传文件至服务器报错“Could not write file”

一、错误说明 使用ftp上传文件 ERROR: Exception when publishing, exception message [Could not write file. Server message: [553 Could not create file.]]11:12:45 FTP: Connecting from host [test-xxx-java-user-service-3-932ft-hsb69-t5wmf] 11:12:45 FTP: Conne…

Openlayers实战:自定义版权属性信息

Openlayers地图中,通常会展示地图的一个版权信息,这里面涉及到地图层的版权信息内容,还涉及到control中的Attribution的设置,本实战示例中,通过灵活的属性配置,显示了还是大剑师兰特的博客版权信息,点击是可以跳转的。 效果图 源代码 /* * @Author: 大剑师兰特(xiaoz…

亚马逊云科技自研芯片,为企业云服务提高性价比

6月27日至28日&#xff0c;2023亚马逊云科技中国峰会于上海顺利召开。在本次峰会上&#xff0c;似乎找寻到了云计算领域竞争对手均日渐成熟&#xff0c;而亚马逊云科技却能一直保持领先地位的原因——过去的十几年里&#xff0c;亚马逊云科技“基于客户需求&#xff0c;快速进行…

2023,中国电商重回元老时代

中国的历史上不缺“太上皇”&#xff0c;但“太上皇”再度站到台前的很少。公元1457年&#xff0c;被囚禁在南宫的“太上皇”朱祁镇复位&#xff0c;上演了中国历史上少见的南宫复辟。而危机时刻被推举为皇帝的朱祁钰&#xff0c;后来的庙号是代宗&#xff0c;阴阳怪气十足。 …

【Unity实战】制作类元气骑士、挺进地牢——俯视角射击游戏多种射击效果(二)

文章目录 前言一、火箭筒1. 编写火箭筒脚本2. 创建火箭弹和新爆炸特效的预制体3. 编写火箭弹脚本4. 设置好火箭弹和火箭筒的脚本和参数5. 运行效果 二、激光枪1. 编写激光枪脚本2. 先运行游戏&#xff0c;看看效果3. 美化射线4. 完善代码5. 再次运行游戏6. 升级URP项目7. 后处理…

Elasticsearch(1)——倒排索引与HTTP操作Elasticsearch

文章目录 1 前言2 Elasticsearch 安装3 数据格式4 倒排索引5 常用HTTP请求操作Elasticsearch5.1 创建索引5.2 查询索引信息5.3 删除索引5.4 创建/修改文档5.5查找文档5.6局部修改文档5.7删除文档5.8分页查询 1 前言 Elastic Stack 核心产品包括 Elasticsearch【存储数据】、Ki…