2016年12月1日 星期四

golang是用同樣的記憶體來存放 for range 取得的變數

在切片裡面走訪的時候,利用range所取出的變數,在記憶體中是同一個位置。不能利用此位置來區別切片裡的內容。例如:


package main

import (
 "fmt"
)

func main() {
 cars := []string{"toyota","honda","bmw"}

 for idx,car := range cars{

        fmt.Printf("car at:%p\n", &car)

        fmt.Printf("cars[%d] at:%p\n", idx,&cars[idx])
 }
}



https://play.golang.org/p/JuH5WdOw7q


這段程式碼跑的結果是這樣:

car at:0x1040a120
cars[0] at:0x10432200
car at:0x1040a120
cars[1] at:0x10432208
car at:0x1040a120
cars[2] at:0x10432210


每次從切片取出來的內容都是放到同一個 car。
因此不能利用car這個變數的位置來作為後續使用,而是要直接用idx索引 cars。



2016年6月20日 星期一

Use context to cancel a function




我曾經使用到一個函數,是要帶入 context.Background。然後這個函數就等在裡面了,直到有回應才回傳結果出來。最近要將這個函數強制的跳出,一時間還不知道該怎麼做。苦惱之際看到了這個函數帶入了 context.Background(),於是就從context這個庫去爬文。於是先找到了


http://studygolang.com/articles/5131


裡面重點的說明了原文裡面 context 的原理與使用方法。原文在這兒:

https://godoc.org/golang.org/x/net/context

還有Google使用context的博客

http://blog.golang.org/context


前情提要講完了,現在直些貼程式碼該怎麼強制跳出一個帶入了context.Background的函數呢?


原本是:

err = aBlockedFunction(context.Background())

想要能夠跳出,就利用WithCancel來生一個cancel函數出來:

ctx,cancel := context.WithCancel( context.Background() )然後放進去
err = aBlockedFunction(ctx)
這樣就能在想要的時機點,呼叫cancel就能夠將旗下的子孫都清除掉,然後從函式返回了。
這也提醒了我,應該來用context來管理資源,這樣子就能夠很容易寫出讓別人cancel的函數了


2016年4月11日 星期一

讀取 http Body 而不會造成 proxy error: http: ContentLength=xx with Body length 0

如果想要看http request內容,看完以後再決定要不要代理到後端服務。要開一個 Proxy 的方法如下:

director := func(req *http.Request) {
    req = r
    req.URL.Scheme = "http"    req.URL.Host = "localhost:2379"}
proxy := &httputil.ReverseProxy{Director: director}
proxy.ServeHTTP(w, r)



在Proxy之前,如果要讀取request body,需要使用ReadAll來把Body讀出來,像這樣子

body,err:=ioutil.ReadAll(reader)

ReadAll返回了資料以後,Body的索引也都會改變。這時去呼叫Proxy的程序則會產生這樣的錯誤: http: proxy error: http: ContentLength=153 with Body length 0
下面的方式是可以讀取Body,而且又不影響到Proxy運作:

b := bytes.NewBuffer(make([]byte, 0))
reader := io.TeeReader(r.Body, b)

body,err:=ioutil.ReadAll(reader)
if err != nil {
    Error(err)
    return}
r.Body = ioutil.NopCloser(b)


為了讓Proxy能夠繼續運作,TeeReader從r.Body複製資料到b裡面並返回一個Reader。過程中不會有其他資料的複製。最後再把複製的資料b放回去r.Body。