HTTP包默認路由匹配規則

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)
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章