深入 Go 中各个高性能 JSON 解析库
转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/535
其实本来我是没打算去看 JSON 库的性能问题的,但是最近我对我的项目做了一次 pprof,从下面的火焰图中可以发现在业务逻辑处理中,有一半多的性能消耗都是在 JSON 解析过程中,所以就有了这篇文章。

这篇文章深入源码分析一下在 Go 中标准库是如何解析 JSON 的,然后再看看有哪些比较流行的 Json 解析库,以及这些库都有什么特点,在什么场景下能更好的帮助我们进行开发。
主要介绍分析以下几个库:
| 库名 | Star |
|---|---|
| 标准库 JSON Unmarshal | |
| valyala/fastjson | 1.2 k |
| tidwall/gjson | 8.3 k |
| buger/jsonparser | 4 k |
json-iterator 库也是一个非常有名的库,但是我测了一下性能和标准库相差很小,相比之下还是标准库更值得使用;
Jeffail/gabs 库与 bitly/go-simplejson 直接用的标准库的 Unmarshal 来进行解析,所以性能上和标准库一致,本篇文章也不会提及;
easyjson这个库需要像 protobuf 一样为每一个结构体生成序列化的代码,具有强入侵性,我个人不是很喜欢,所以也没提及。
上面的这些库是我能搜到的 Star 数大于 1k 比较知名,并且仍然在迭代的 JSON 解析库,如果有遗漏的,可以联系我,我会补上。
标准库 JSON Unmarshal
分析
func Unmarshal(data []byte, v interface{
})
官方的 JSON 解析库需要传两个参数,一个是需要被序列化的对象,另一个是表示这个对象的类型。
在真正执行 JSON 解析之前会调用 reflect.ValueOf来获取参数 v 的反射对象。然后会获取到传入的 data 对象的开头非空字符来界定该用哪种方式来进行解析。
func (d *decodeState) value(v reflect.Value) error {
switch d.opcode {
default:
panic(phasePanicMsg)
// 数组
case scanBeginArray:
...
// 结构体或map
case scanBeginObject:
...
// 字面量,包括 int、string、float 等
case scanBeginLiteral:
...
}
return nil
}
如果被解析的对象是以[开头,那么表示这是个数组对象会进入到 scanBeginArray 分支;如果是以{
开头,表明被解析的对象是一个结构体或 map,那么进入到 scanBeginObject 分支 等等。
以解析对象为例:
func (d *decodeState) object(v reflect.Value) error {
...
var fields structFields
// 检验这个对象的类型是 map 还是 结构体
switch v.Kind() {
case reflect.Map:
...
case reflect.Struct:
// 缓存结构体的字段到 fields 对象中
fields = cachedTypeFields(t)
// ok
default:
d.saveError(&UnmarshalTypeError{
Value: "object", Type: t, Offset: int64(d.off)})
d.skip()
return nil
}
var mapElem reflect.Value
origErrorContext := d.errorContext
// 循环一个个解析JSON字符串中的 key value 值
for {
start := d.readIndex()
d.rescanLiteral()
item := d.data[start:d.readIndex()]
// 获取 key 值
key, ok := unquoteBytes(item)
if !ok {
panic(phasePanicMsg)
}
var subv reflect.Value
destring := false
...
// 根据 value 的类型反射设置 value 值
if destring {
// value 值是字面量会进入到这里
switch qv := d.valueQuoted().(type) {
case nil:
if err := d.literalStore(nullLiteral, subv, false); err != nil {
return err
}
case string:
if err := d.literalStore([]byte(qv), subv, true); err != nil {
return err
}
default:
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type()))
}
} else {
// 数组或对象会递归调用 value 方法
if err := d.value(subv); err !=

本文深入探讨了Go中几个高性能的JSON解析库,包括标准库JSON Unmarshal、fastjson、GJSON和jsonparser。通过对源码的分析,揭示了它们在性能和功能上的特点。标准库虽然功能全面但性能较低,而fastjson和GJSON提供了更快的速度,其中fastjson更注重速度,GJSON则提供了一些额外的功能,如模糊匹配。jsonparser则在保持高性能的同时,提供了自定义回调函数的能力。
最低0.47元/天 解锁文章
1769

被折叠的 条评论
为什么被折叠?



