在 serverHandler 的 ServeHTTP() 方法里的 sh.srv.Handler 其实就是我们最初在 http.ListenAndServe() 中传入的 Handler 对象,也就是我们自定义的 ServeMux 对象。如果该 Handler 对象为 nil ,则会使用默认的 DefaultServeMux 。最后调用 ServeMux 的 ServeHTTP() 方法匹配当前路由对应的 handler 方法。
后面的逻辑就相对简单清晰了,主要在于调用 ServeMux 的 match 方法匹配到对应的已注册的路由表达式和 handler 。
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
// Find a handler on a handler map given a path string.
// Most-specific (longest) pattern wins.
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
// Check for exact match first.
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
// Check for longest valid match. mux.es contains all patterns
// that end in / sorted from longest to shortest.
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
在 match 方法里我们看到之前提到的 map[string]muxEntry 和 []muxEntry 。这个方法里首先会利用进行精确匹配,在 map[string]muxEntry 中查找是否有对应的路由规则存在;如果没有匹配的路由规则,则会进行近似匹配。
对于类似 /path1/path2/path3 这样的路由,如果不能找到精确匹配的路由规则,那么则会去匹配和当前路由最接近的已注册的父路由,所以如果路由 /path1/path2/ 已注册,那么该路由会被匹配,否则继续匹配父路由,知道根路由 / 。
由于 []muxEntry 中的 muxEntry 按照路由表达是从长到短排序,所以进行近似匹配时匹配到的路由一定是已注册父路由中最接近的。
至此,Go实现的 http server 的大致原理介绍完毕!
总结
Golang通过 ServeMux 定义了一个多路器来管理路由,并通过 Handler 接口定义了路由处理函数的统一规范,即









