GOLANG 中HTTP包預設路由匹配規則閱讀筆記
一、執行流程
構建一個簡單http server:
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/
就可以看到輸出了
通過跟蹤http.go包程式碼,可以發現執行流程基本如下:
1.建立一個Listener
監聽8080
埠
2.進入for
迴圈並Accept請求,沒有請求則處於阻塞狀態
3.接收到請求,並建立一個conn物件,放入goroutine處理(實現高併發關鍵)
4.解析請求來源資訊獲得請求路徑等重要資訊
5.請求ServerHTTP方法,已經通過上一步獲得了ResponseWriter和Request物件
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處理
http請求處理流程更多資訊可以參考《Go Web 程式設計 》3.3 Go如何使得Web工作
二、DefaultServeMux 路由匹配規則
先看幾個路由規則:
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)) }
情景一:
訪問:http://127.0.0.1:8080/
返回:hello world
情景二:
訪問: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.我設定了這麼多規則為什麼規則一可以通用匹配未設定的路由資訊,而且又不影響已經存在路由, 內部是怎麼實現的?
2.1 新增路由規則
先看兩個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/
2.2 查詢路由規則
路由規則的查詢就是從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的執行。