HTTP包默認路由匹配規則
內容簡介:最近看到 http 包的相關內容,寫了幾個路由發現規則好像不是正則匹配,下面從源碼觸發分析下路由匹配和執行的過程上面的代碼的執行情況如下,對於一般中間件的結構如下
最近看到 http 包的相關內容,寫了幾個路由發現規則好像不是正則匹配,下面從源碼觸發分析下路由匹配和執行的過程
問題引入
//路由1 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "/") }) //路由2 http.HandleFunc("/path/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "/path/") }) //路由3 http.HandleFunc("/path/subpath", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "/path/subpath") }) // 傳入nil,使用默認的DefaultServeMux中間件 http.ListenAndServe(":8080", nil)
上面的代碼的執行情況如下,對於 /path
和 /path/subpath/
的結果是不是很意外
127.0.0.1:8080 輸出 / 127.0.0.1:8080/abc/def 輸出 / 127.0.0.1:8080/path 輸出 /path/ 127.0.0.1:8080/path/ 輸出 /path/ 127.0.0.1:8080/path/subpath 輸出 /path/subpath 127.0.0.1:8080/path/subpath/ 輸出 /path/
源碼解讀
一般中間件的結構如下
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry // 所有註冊的路由 es []muxEntry // 所有以/結尾的路由規則,有長到短排序 hosts bool }
先從 http.HandleFunc
路由註冊追蹤,一層一層往下點,找到如下代碼
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) } e := muxEntry{h: handler, pattern: pattern} // 所有註冊的路由存放到mux.m裏面 mux.m[pattern] = e // 如果路由以/做結尾,則在mux.es中存放一份規則,同時做好從長到短的排序,便於以後 if pattern[len(pattern)-1] == '/' { mux.es = appendSorted(mux.es, e) } if pattern[0] != '/' { mux.hosts = true } }
路由註冊完後進入到 ListenAndServe
中,層層往下找,找到如下循環等待請求的代碼
func (c *conn) serve(ctx context.Context)
中又執行了 serverHandler{c.server}.ServeHTTP(w, w.req)
func (srv *Server) Serve(l net.Listener) error { // ... for { // ... // 啓動一個goroutine處理請求 go c.serve(ctx) } }