http包提供了HTTP客戶端和服務端的實現。golang
package main import ( "fmt" "log" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Println("hello") }) log.Fatal(http.ListenAndServe("localhost:8000", nil)) }
咱們先分析下面這個函數web
咱們首先來了解下Handler類型 其定義以下編程
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
也就是說凡是實現了 ServeHTTP(ResponseWriter, *Request)方法的類型都是知足Handler接口的,因此咱們能夠用下面的方式來實現一個web服務併發
(上面的說法並不嚴謹,我會在最後講述使用http.HandlerFunc()進行類型轉換來實現知足Handler接口的方法)tcp
import ( "fmt" "log" "net/http" ) type myHandler struct { } func (h myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/": fmt.Println("hello") default: w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "not found: %s\n", r.URL) } } func main() { handler := myHandler{} log.Fatal(http.ListenAndServe("localhost:8000", handler)) }
可能有人注意到了咱們在最開始寫的,最簡單的web服務中的http.ListenAndServe("localhost:8000", nil)咱們向第二個參數傳遞的是nil,並不知足http.Handler接口,這個問題咱們留到下面去解答函數
ServeMux類型是HTTP請求的多路轉接器。它會將每個接收的請求的URL與一個註冊模式的列表進行匹配,並調用和URL最匹配的模式的處理器。.net
上面的例子中myHandler.ServeHTTP()實際上既實現了路由分配又實現了邏輯處理。在大多數狀況下,咱們更但願不一樣的路由交由不一樣的方法來處理,因此http包中引入了ServeMux類型來幫助咱們實現路由分配code
type myHandler struct { } func (h myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hello world") } func main() { handler := myHandler{} mux := http.NewServeMux() mux.Handle("/", handler) log.Fatal(http.ListenAndServe("localhost:8000", mux)) }
type ServeMux struct { mu sync.RWMutex //鎖,因爲請求涉及到併發處理,所以這裏須要一個鎖機制 m map[string]muxEntry // 路由規則,一個string對應一個mux實體,這裏的string就是註冊的路由表達式 hosts bool // 是否在任意的規則中帶有host信息 } type muxEntry struct { explicit bool // 是否精確匹配 h Handler // 這個路由表達式對應哪一個handler pattern string //匹配字符串 }
能夠注意到muxEntry中的h的類型爲實現Handler接口結構的對象對象
經過ServeMux能夠看到實際的路由規則記錄在ServeMux.m 中,這個屬性是一個map[string]muxEntry 類型,string記錄都是路由,muxEntry裏包含實際的handlerblog
經過http.ListenAndServe("localhost:8000", mux)很容易想到ServeMux實現了也實現了Handler接口(擁有ServerHTTP()方法)
因此實現路由解析的原理爲
源碼入下
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { w.Header().Set("Connection", "close") w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) h.ServeHTTP(w, r) } func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { if r.Method != "CONNECT" { if p := cleanPath(r.URL.Path); p != r.URL.Path { _, pattern = mux.handler(r.Host, p) return RedirectHandler(p, StatusMovedPermanently), pattern } } return mux.handler(r.Host, r.URL.Path) } 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 }
經過上面路由解析的原理能夠了解:據用戶請求的URL和路由器裏面存儲的map去匹配,當匹配到以後返回存儲的handler,調用這個handler的ServeHTTP接口就能夠執行到相應的函數了。
而將URL和handler 添加到map中則須要使用func (mux *ServeMux) Handle(pattern string, handler Handler)
看上面用法也能夠很容易理解 mux.Handle("/", handler) 函數將URL:"/" 添加到了map的鍵,將對應的handler 添加到了muxEntry結構中
還記得上面的說法:凡是實現了ServeHTTP(ResponseWriter, *Request) 方法的類型都是知足Handler接口的。但實際開發中咱們會發現大部分handler 並無實現ServeHTTP() 的函數,而是以下的一種方式
type myHandler struct { } func (h myHandler) index(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hello world") } func (h myHandler) News(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "news one") } func main() { h := myHandler{} mux := http.NewServeMux() mux.Handle("/", http.HandlerFunc(h.index)) mux.Handle("/news", http.HandlerFunc(h.News)) log.Fatal(http.ListenAndServe("localhost:8000", mux)) }
http.HandlerFunc(h.index)在這裏並非函數調用,而是強制的類型轉換
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
即咱們調用了HandlerFunc(f),強制類型轉換f成爲HandlerFunc類型,這樣f就擁有了ServeHTTP方法。
由於像上面http.HandlerFunc()方式是很是廣泛的,因此下面兩個是等價的
mux.HandleFunc("/", h.index) mux.Handle("/news", http.HandlerFunc(h.News))
注意和上面的http.HandlerFunc()進行區分,前者是實現了Handler接口的類型,可以使用強制類型轉換http.HandlerFunc(f) 讓f擁有ServeHTTP方法。
後者是爲默認的ServeMux:DefaultServeMux註冊路由和handler
http.ListenAndServe("localhost:8000", nil)中第二參數爲nil時,http.Serve()便使用DefaultServeMux做爲處理的handler
經過對http包的分析以後,如今讓咱們來梳理一下整個的代碼執行過程。 首先調用Http.HandleFunc 按順序作了幾件事:
其次調用http.ListenAndServe(":9090", nil) 按順序作了幾件事情:
實例化Server
調用Server的ListenAndServe()
調用net.Listen("tcp", addr)監聽端口
啓動一個for循環,在循環體中Accept請求
對每一個請求實例化一個Conn,而且開啓一個goroutine爲這個請求進行服務go c.serve()
讀取每一個請求的內容w, err := c.readRequest()
判斷handler是否爲空,若是沒有設置handler(這個例子就沒有設置handler),handler就設置爲DefaultServeMux
調用handler的ServeHttp
在這個例子中,下面就進入到DefaultServeMux.ServeHttp
根據request選擇handler,而且進入到這個handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)
選擇handler: