ServeMux扮演的角色是Multiplexer,它用来将将请求根据url路由给已注册的handler。以下图:web
上图中为3个路径注册了handler,一个是"/",另外两个是"/hello"和"/world"。这表示访问http://hostname/hello
时,multiplexer会调用上图中对应的第二个handler,当访问http://hostname/world
时,multiplexer会调用上图中对应的第三个handler,当不是这两个路径时,将调用第一个绑定在"/"上的handler。浏览器
注意,go的mux路由请求时,handler绑定的路径是否带尾随"/"是不同的。带上尾随"/",表示此路径以及此路径下的子路径,都会调用注册在此路径上的handler。函数
例如,当请求uri为"/hello/abc"的时候,不会调用"/hello"对应的handler,而是调用"/"对应的handler。只有注册handler的路径为"/hello/"时,uri为"/hello/abc"才会调用此handler。工具
实际上,当注册handler的路径带上尾随斜"/"时,在发起此路径的请求时,会经过301重定向的方式自动补齐这个尾随斜线,让浏览器发起第二次请求。例如,下面是注册handler的路径:url
http.Handle("/hello/", &myHandler)
发起http://hostname/hello
的请求时,会自动补齐为http://hostname/hello/
,而后浏览器自动发起第二次请求。code
ServeMux对每次流入的http请求的URL进行模式(pattern)匹配,而后调用注册在此pattern上的handler来处理这个请求。server
Pattern部分能够定义为匹配host的模式。若是pattern以"/"开头,表示匹配URL的路径部分,若是不以"/"开头,表示从host开始匹配。blog
匹配时选择匹配匹配度最高(长匹配优先于短匹配)。例如为"/images/"注册了handler1,"/images/thumbnails/"注册了handler2,若是请求的URL路径部分为"/images/thumbnails/",将会调用handler2处理这个请求,若是请求的URL路径部分为"/images/foo/",将调用handler1处理。ip
注意,注册在"/"上的pattern会在其它模式匹配不上时被选中,由于全部请求均可以匹配这个pattern,只不过能匹配到的长度最短。路由
若是pattern带上了尾随斜线"/",ServeMux将会对请求不带尾随斜线的URL进行301重定向。例如,在"/images/"模式上注册了一个handler,当请求的URL路径为"/images"时,将自动重定向为"/images/"。除非再单独为"/images"模式注册一个handler。
若是为"/images"注册了handler,当请求URL路径为"/images/"时,将没法匹配该模式。
看看net/http/server.go文件中ServeMux的结构:
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool // whether any patterns contain hostnames } type muxEntry struct { h Handler pattern string }
结构看上去很简单。一个字段mu是RWMutex,m是注册handler和pattern的,hosts用于判断pattern是否包含了host的匹配。看看Handle()函数的定义会更清晰:
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) } mux.m[pattern] = muxEntry{h: handler, pattern: pattern} if pattern[0] != '/' { mux.hosts = true } } // HandleFunc registers the handler function for the given pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) }
pattern为空或者handler为空时,都会panic。此外,想要定义重复的pattern,也会panic。若是pattern的第一个字符不是"/",则表示这个pattern是从主机名开始匹配的。
惟一须要注意的是,每一个Handle()都会对ServeMux实例加上写锁。
以经常使用的DefaultServeMux为例,它是ServeMux的一个实例。当使用DefualtServeMux时,每调用一次Handle()或HandleFunc(),都意味着向这个DefaultServeMux的结构中的m字段添加pattern和对应的handler。因为加了写锁,若是使用多个goroutine同时启动多个web服务,在同一时刻将只能有一个goroutine启动的web服务能设置DefaultServeMux。固然,通常状况下不会使用goroutine的方式同时启动多个web服务。
自带的默认的DefaultServeMux其实功能限制很大。好比请求的URL路径为"/images/123.png",想要匹配这个确实容易,可是想要取出其中的"123.png"字符串,DefaultServeMux就无法实现。
有一个很是强大的Gorilla工具包(www.gorillatoolkit.org),它有好几个功能,其中一个功能是提供multiplexer。