構建一個簡單http server:git
package main import ( "log" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world")) }) log.Fatal(http.ListenAndServe(":8080", nil)) }
使用http://127.0.0.1:8080/
就能夠看到輸出了github
經過跟蹤http.go包代碼,能夠發現執行流程基本以下:golang
Listener
監聽8080
端口for
循環並Accept請求,沒有請求則處於阻塞狀態func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { //此handler即爲http.ListenAndServe 中的第二個參數 handler := sh.srv.Handler if handler == nil { //若是handler爲空則使用內部的DefaultServeMux 進行處理 handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } //這裏就開始處理http請求 //若是須要使用自定義的mux,就須要實現ServeHTTP方法,即實現Handler接口。 handler.ServeHTTP(rw, req) }
6.進入DefaultServeMux中的邏輯就是根據請求path在map中匹配查找handler,並交由handler處理web
http請求處理流程更多信息能夠參考[《Go Web 編程
》3.3 Go如何使得Web工做](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/03.3.md)編程
先看幾個路由規則:併發
package main import ( "log" "net/http" ) func main() { //規則1 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world")) }) //規則2 http.HandleFunc("/path/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pattern path: /path/ ")) }) //規則3 http.HandleFunc("/path/subpath", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("pattern path: /path/subpath")) }) log.Fatal(http.ListenAndServe(":8080", nil)) }
情景一:app
訪問:http://127.0.0.1:8080/
函數
返回:hello world
高併發
情景二:ui
訪問:http://127.0.0.1:8080/path
返回:pattern path: /path/
情景三:
訪問:http://127.0.0.1:8080/path/subpath/
返回:pattern path: /path/
情景四:
訪問:http://127.0.0.1:8080/hahaha/
返回:hello world
先說明一些規則吧,再看代碼是怎麼實現的:
1.若是匹配路徑中後帶有/
,則會自動增長一個匹配規則不帶/
後綴的,並跳轉轉到path/
,解釋了情景二的場景,爲何匹配到的/path/
2.我設置了這麼多規則爲何規則一能夠通用匹配未設置的路由信息,並且又不影響已經存在路由, 內部是怎麼實現的?
先看兩個struct,這是存放默認路由規則的:
type ServeMux struct { mu sync.RWMutex //處理併發,增長讀寫鎖 m map[string]muxEntry //存放規則map,key即爲設置的path hosts bool // whether any patterns contain hostnames(是否包含host) } type muxEntry struct { explicit bool //是否徹底匹配 h Handler//相應匹配規則的handler pattern string//匹配路徑 }
經過跟蹤http.HandleFunc
定位到以下代碼,正是往上面兩個struct
中增長規則:
func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern " + pattern) } if handler == nil { panic("http: nil handler") } //若是已經匹配到了則panic if mux.m[pattern].explicit { panic("http: multiple registrations for " + pattern) } //增長一個新的匹配規則 mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} //根據path的第一個字母判斷是否有host if pattern[0] != '/' { mux.hosts = true } //!!這裏看清楚 就是實現了情景二的狀況 ,看判斷條件 n := len(pattern) if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit{ // If pattern contains a host name, strip it and use remaining // path for redirect. path := pattern if pattern[0] != '/' { // In pattern, at least the last character is a '/', so // strings.Index can't be -1. path = pattern[strings.Index(pattern, "/"):] } url := &url.URL{Path: path} mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern} } }
上面有個Helpful behavior
的註釋行爲,就是實現了情景二的狀況,他是判斷若是匹配的路徑中最後含有/
,而且以前也不存在添加去除反斜槓的規則的話,就自動給他增長一個301的跳轉指向/path/
路由規則的查找就是從ServeMux
中的map去匹配查找的,的到這個handler並執行,只是會有一些處理機制,好比怎麼樣確保訪問/path/subpath
的時候是先匹配/path/subpath
而不是匹配/path/
呢?
當一個請求過來的時候,跟蹤到了mux.match
方法:
過程
mux.ServerHTTP
->mux.Handler
->mux.handler
->mux.match
func (mux *ServeMux) match(path string) (h Handler, pattern string) { var n = 0 for k, v := range mux.m { if !pathMatch(k, path) { continue } //若是匹配到了一個規則,並無立刻返回handler,並且繼續匹配而且判斷path的長度是不是最長的,這是關鍵!!! if h == nil || len(k) > n { n = len(k) h = v.h pattern = v.pattern } } return }
1.這裏就解釋了爲何設置的精確的path是最優匹配到的,由於它是根據path的長度判斷。
固然也就解釋了爲何/
能夠匹配全部(看pathMatch
函數就知道了,/
是匹配全部的,只是這是最後才被匹配成功)
2.獲得了處理請求的handler,再調用h.ServeHTTP(w, r)
,去執行相應的handler方法。
等一下,handler中哪裏有ServeHTTP
這個方法??
由於在調用 http.HandleFunc
的時候已經將自定義的handler處理函數,強制轉爲HandlerFunc
類型的,就擁有了ServeHTTP
方法:
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
f(w,r)
就實現了handler的執行。
原文地址:silenceper.com