今天發現,go的優點除了它的輕量線程(goroutine)提供了更方便靈活的併發編程模式以外,它的I/O機制也設計的很是給力。編程
以前,我在向其餘服務器發送json數據時,都須要先聲明一個bytes
緩存,而後經過json
庫把結構體中的內容mashal成字節流,再經過Post函數發送。
代碼以下:json
package main import ( "bytes" "encoding/json" "io/ioutil" "log" "net/http" ) func init() { log.SetFlags(log.Lshortfile) } func main() { cli := http.Client{} msg := struct { Name, Addr string Price float64 }{ Name: "hello", Addr: "beijing", Price: 123.56, } buf := bytes.NewBuffer(nil) json.NewEncoder(buf).Encode(msg) resp, err := cli.Post("http://localhost:9999/json", "application/json", buf) if err != nil { log.Fatalln(err) } body := resp.Body defer body.Close() if body_bytes, err := ioutil.ReadAll(body); err == nil { log.Println("response:", string(body_bytes)) } else { log.Fatalln(err) } }
這樣就老是須要咱們在內存中預先分配一個緩存,大部分狀況下其實還好,BUT 若是須要發送的數據很大,就會嚴重影響性能了。緩存
go設計了一個Pipe函數幫咱們解決這個問題,因而個人代碼能夠改成:服務器
package main import ( "encoding/json" "io" "io/ioutil" "log" "net/http" "time" ) func init() { log.SetFlags(log.Lshortfile) } func main() { cli := http.Client{} msg := struct { Name, Addr string Price float64 }{ Name: "hello", Addr: "beijing", Price: 123.56, } r, w := io.Pipe() // 注意這邊的邏輯!! go func() { defer func() { time.Sleep(time.Second * 2) log.Println("encode完成") // 只有這裏關閉了,Post方法纔會返回 w.Close() }() log.Println("管道準備輸出") // 只有Post開始讀取數據,這裏纔開始encode,並傳輸 err := json.NewEncoder(w).Encode(msg) log.Println("管道輸出數據完畢") if err != nil { log.Fatalln("encode json failed:", err) } }() time.Sleep(time.Second * 1) log.Println("開始從管道讀取數據") resp, err := cli.Post("http://localhost:9999/json", "application/json", r) if err != nil { log.Fatalln(err) } log.Println("POST傳輸完成") body := resp.Body defer body.Close() if body_bytes, err := ioutil.ReadAll(body); err == nil { log.Println("response:", string(body_bytes)) } else { log.Fatalln(err) } }
輸出以下:併發
main.go:35: 管道準備輸出 main.go:44: 開始從管道讀取數據 main.go:38: 管道輸出數據完畢 main.go:31: encode完成 main.go:50: POST傳輸完成 main.go:56: response: {"Name":"hello","Addr":"beijing","Price":123.56}
能夠看到,經過Pipe,咱們能夠方便的連接輸入和輸出,只要咱們能正確理解整個過程的邏輯。有了它,咱們終於能夠甩去討厭的中間緩存了,同時也提升了系統的穩定性。app
爲了方便你們調試,如下是服務器端代碼:函數
package main import ( "encoding/json" "io/ioutil" "log" "net/http" ) func init() { log.SetFlags(log.Lshortfile) } func main() { http.HandleFunc("/json", handleJson) http.ListenAndServe(":9999", nil) } func handleJson(resp http.ResponseWriter, req *http.Request) { if req.Method == "POST" { body := req.Body defer body.Close() body_bytes, err := ioutil.ReadAll(body) if err != nil { log.Println(err) resp.Write([]byte(err.Error())) return } j := map[string]interface{}{} if err := json.Unmarshal(body_bytes, &j); err != nil { log.Println(err) resp.Write([]byte(err.Error())) return } resp.Write(body_bytes) } else { resp.Write([]byte("請使用post方法!")) } }