golang官方为咱们提供了标准的json解析库–encoding/json
,大部分状况下,使用它已经够用了。不过这个解析包有个很大的问题–性能。它不够快,若是咱们开发高性能、高并发的网络服务就没法知足,这时就须要高性能的json解析库,目前性能比较高的有json-iterator
和easyjson
。git
如今咱们须要引进一个高性能的json解析库,这里以json-iterator
为例,可是咱们所有换掉又不放心,因此能够先小范围的测试下,这时候咱们就须要两个解析库并存,那么这时候咱们如何选择咱们须要的解析库编译和运行呢?github
解决上面问题的办法就是条件编译。Go语言为咱们提供了基于tags的编译约束来解决这个问题。golang
咱们先举个例子看看结果。如今咱们须要两个库并存,因此咱们先得统一这两个库的用法(参考适配器模式),这里咱们使用一个自定义的json
包来适配encoding/json
和json-iterator
。json
json/json.gobash
// +build !jsoniter package json import ( "encoding/json" "fmt" ) func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { fmt.Println("Use [encoding/json] package") return json.MarshalIndent(v,prefix,indent) }
json/jsoniter.go网络
// +build jsoniter package json import ( "fmt" "github.com/json-iterator/go" ) var ( json = jsoniter.ConfigCompatibleWithStandardLibrary ) func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { fmt.Println("Use [jsoniter] package") return json.MarshalIndent(v,prefix,indent) }
目录结构以下:并发
json ├── json.go └── jsoniter.go
例子中以MarshalIndent
函数为例,咱们发现json
包下的两个go文件中都有MarshalIndent
函数的定义,而且签名一致,可是它们又是使用不一样的json解析库实现,这就是咱们统一适配包装后的结果,调用统一了。函数
为了区分调用的是哪一个json库的具体实现,打印日志,以便区分。如今咱们使用json.MarshalIndent
测试一下。高并发
package main import ( "fmt" "json" ) func main() { u := user{"Mike", 30} b, err := json.MarshalIndent(u, "", " ") if err != nil { fmt.Println(err) } else { fmt.Println(string(b)) } } type user struct { Name string Age int }
使用很简单,把一个user
结构体对象转为json字符串,并打印出来。咱们运行go run main.go
看看结果。性能
Use [encoding/json] package { "Name": "Mike", "Age": 30 }
保持咱们默认使用encoding/json
库的方式不变。如今咱们换一种编译运行方式:
go run -tags=jsoniter main.go
此次运行和上次不一样的地方在于咱们加了-tags=jsoniter
,而后就使用了json-iterator
这个json库,这就是选择性的条件编译,达到了咱们小部分测试新的json库的目的。
咱们发现,条件编译的关键在于-tags=jsoniter
,也就是-tags
这个标志,这就是Go语言为咱们提供的条件编译的方式之一。
好了,回过头来看咱们刚开始时json/json.go
、json/jsoniter.go
这两个Go文件的顶部,都有一行注释:
// +build !jsoniter // +build jsoniter
这两行是Go语言条件编译的关键。+build
能够理解为条件编译tags的声明关键字,后面跟着tags的条件。
// +build !jsoniter
表示,tags不是jsoniter
的时候编译这个Go文件。 // +build jsoniter
表示,tags是jsoniter
的时候编译这个Go文件。
也就是说,这两种条件是互斥的,只有当tags=jsoniter
的时候,才会使用json-iterator
,其余状况使用encoding/json
。
利用条件编译,咱们实现了灵活选择json解析库的目的,且tags只是其中的一部分,Go语言还能够根据Go文件后缀进行条件编译。