Go Web:自帶的ServeMux multiplexer

ServeMux簡介

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的匹配規則

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/"時,將沒法匹配該模式。

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。

相關文章
相關標籤/搜索