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文件後綴進行條件編譯。