先貼一段代碼
package mainimport ( "net/http" "fmt" "log")func hello(w http.ResponseWriter, r *http.Request) { r.ParseForm() fmt.Printf("%+v\n", *r.URL) fmt.Fprintln(w, "Hello world")}func main() { http.HandleFunc("/", hello) err := http.ListenAndServe(":9090", nil) if err != nil { log.Fatal(err) }}
這裡監聽本地的9090連接埠,使用 http.HandleFunc 將URL為“/”的請求將其轉向名為hello的方法。這是比較常見的Golang的簡單Web的實現,但是看著會覺得很奇怪,建議先把代碼跑起來,然後我們再來看看源碼中Handler的定義先。
type Handler interface { ServeHTTP(ResponseWriter, *Request)}
如代碼中所示,Handler實際上一個介面,實現了 ServeHTTP(ResponseWriter, *Request) 這個函數,就相當於實現了一個Handler。通俗講Handler就是處理輸入輸出資料流的函數,將Handler註冊到路由器,等同於將特定的URL跟這個函數綁定起來,當用戶端請求訪問這個URL,路由器就會使用這個函數進行操作。
可能會有童鞋會疑問,hello這個函數並不是一個Handler,為什麼可以作為Handler註冊一個URL。這是因為這裡有一個函數叫做HandleFunc,這裡就講講HandleFunc的原理吧。先把源碼列出來:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}
HandleFunc傳入一個字串(URL)和一個func(ResponseWriter, Request),hello這個函數是符合func(ResponseWriter, Request)的要求的,然後使用DefaultServeMux進行處理,DefaultServeMux又是什嗎?在server.go的源碼中可以輕鬆找到DefaultServeMux的定義
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool // whether any patterns contain hostnames}....// DefaultServeMux is the default ServeMux used by Serve.var DefaultServeMux = &defaultServeMuxvar defaultServeMux ServeMux
由源碼可以知道DefaultServeMux相當於一個公用的路由,一個公有變數,URL和Handle就是註冊到這個路由上的。需要注意的是ServeMux的成員變數,有一個sync.RWMutex,瞭解過Golang並發的應該知道,這是一個讀寫鎖(Mutiple read,single write lock, 多讀單寫鎖,允許多個讀操作並存執行,但寫操作會完全互斥,在讀取操作比較頻繁時比sync.Mutex有著更好的效能表現),這應該也是考慮到作為Web伺服器,讀取操作會比較頻繁。接下來看mux.Handle(pattern, HandlerFunc(handler))去到哪一步,應該看到這裡有一個函數HandlerFunc(handler):
type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}
看到這裡你就應該明白,為什麼hello這個函數沒有實現Handler方法也是一個Handler,因為HandlerFunc把hello強制實現ServeHTTP(w ResponseWriter, r *Request),所以hello這個函數也就是一個Handler。
這裡是路由的最後一步了
type muxEntry struct { h Handler pattern string}// Handle registers the handler for the given pattern.// If a handler already exists for pattern, Handle panics.func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern") } if handler == nil { panic("http: nil handler") } if _, exist := mux.m[pattern]; exist { panic("http: multiple registrations for " + pattern) } if mux.m == nil { mux.m = make(map[string]muxEntry) } mux.m[pattern] = muxEntry{h: handler, pattern: pattern} if pattern[0] != '/' { mux.hosts = true }}
公有變數DefaultServeMux將Handler註冊,這裡就用到了讀寫鎖以及一個私人結構muxEntry,到這裡,路由註冊就結束了。