Go Web:自帶的ServeMux multiplexer


ServeMux簡介

ServeMux扮演的角色是Multiplexer,它用來將將請求根據url路由給已注冊的handler。如下圖:

上圖中為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的路徑:

http.Handle("/hello/", &myHandler)

發起http://hostname/hello的請求時,會自動補齊為http://hostname/hello/,然后瀏覽器自動發起第二次請求。

ServeMux的匹配規則

ServeMux對每次流入的http請求的URL進行模式(pattern)匹配,然后調用注冊在此pattern上的handler來處理這個請求。

Pattern部分可以定義為匹配host的模式。如果pattern以"/"開頭,表示匹配URL的路徑部分,如果不以"/"開頭,表示從host開始匹配。

匹配時選擇匹配匹配度最高(長匹配優先於短匹配)。例如為"/images/"注冊了handler1,"/images/thumbnails/"注冊了handler2,如果請求的URL路徑部分為"/images/thumbnails/",將會調用handler2處理這個請求,如果請求的URL路徑部分為"/images/foo/",將調用handler1處理。

注意,注冊在"/"上的pattern會在其它模式匹配不上時被選中,因為所有請求都可以匹配這個pattern,只不過能匹配到的長度最短。

如果pattern帶上了尾隨斜線"/",ServeMux將會對請求不帶尾隨斜線的URL進行301重定向。例如,在"/images/"模式上注冊了一個handler,當請求的URL路徑為"/images"時,將自動重定向為"/images/"。除非再單獨為"/images"模式注冊一個handler。

如果為"/images"注冊了handler,當請求URL路徑為"/images/"時,將無法匹配該模式。

ServeMux詳細解釋

看看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服務。

第三方ServeMux

自帶的默認的DefaultServeMux其實功能限制很大。比如請求的URL路徑為"/images/123.png",想要匹配這個確實容易,但是想要取出其中的"123.png"字符串,DefaultServeMux就沒法實現。

有一個非常強大的Gorilla工具包(www.gorillatoolkit.org),它有好幾個功能,其中一個功能是提供multiplexer。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM