深入 Go 中各个高性能 JSON 解析库

深入 Go 中各个高性能 JSON 解析库

转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/535

其实本来我是没打算去看 JSON 库的性能问题的,但是最近我对我的项目做了一次 pprof,从下面的火焰图中可以发现在业务逻辑处理中,有一半多的性能消耗都是在 JSON 解析过程中,所以就有了这篇文章。

深入 Go 中各个高性能 JSON 解析库_第1张图片

这篇文章深入源码分析一下在 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 != nil {
   
				return err
			

你可能感兴趣的:(golang,json)