這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在上一篇裡面我們通過:
func main() { http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "hello") }) http.ListenAndServe(":9010", nil)}
分析了 http.handleFunc 的路由調用之間的關係。這次。我們分析一下 http.ListenAndServe():
http.ListenAndServe(addr string, handler Handler)
函數的內部實現:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}
發現這個http.ListenAndServe 其實調用的是 Server.ListenAndServe。我們先來看看 Server的結構:
type Server struct { Addr string // TCP address to listen on, ":http" if empty Handler Handler //處理器,如果為空白則使用 http.DefaultServeMux ReadTimeout time.Duration WriteTimeout time.Duration ....}
看到這個Handler,再聯絡上次我們分析的http.HandleFunc。我們發現他們預設都使用了 http.DefaultServeMux 這個路由處理器。而在這個處理器裡面 剛好儲存了我們註冊的一個路由。/hello。然後我們看看 server.ListenAndServe 是怎麼監聽和分發路由的。
func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})}
在 server.ListenAndServe 裡面註冊了一個tcp的監聽器,監聽我們註冊的網路連接埠。接著繼續調用
server.Serve(l net.Listener) error
進行服務。
func (srv *Server) Serve(l net.Listener) error { ....... for { rw, e := l.Accept() .... c := srv.newConn(rw) c.setState(c.rwc, StateNew) // before Serve can return go c.serve(ctx) }}
開啟一個for迴圈,接受net.accept()。接著使用了 srv.newConn(net.conn) 把一個tcp的conn轉換成一個http.server#conn:
func (srv *Server) newConn(rwc net.Conn) *conn { c := &conn{ server: srv, rwc: rwc, } .... return c}
最後開啟一個go協程對每個請求進行處理。
func (c *conn) serve(ctx context.Context) { // 用戶端主機ip c.remoteAddr = c.rwc.RemoteAddr().String() .... // HTTP/1.x from here on. //讀取請求的資料 c.r = &connReader{r: c.rwc} c.bufr = newBufioReader(c.r) c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10) ctx, cancelCtx := context.WithCancel(ctx) defer cancelCtx() for { w, err := c.readRequest(ctx) ...... serverHandler{c.server}.ServeHTTP(w, w.req) ... }}
請求經過七倒八倒,最後進入了:
serverHandler{c.server}.ServeHTTP(w, w.req)
並且調用了它裡面的ServerHandler.serveHTTP。果斷點開這個函數,豁然開朗,原來是調用了最上層:
http.DefaultServeMux.Handle
不信看這裡:
type serverHandler struct { srv *Server}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { //sh.srv.Handler =server.handler handler := sh.srv.Handler if handler == nil { //我們最初傳的參數就是 nil handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req)}
所以啊,最後的處理是函數是 路由裡面的 ServeMux.ServeHTTP,昨晚我們已經分析了ServeMux.ServeHTTP執行的是 我們自己傳進去的函數。 伺服器 開啟以後一個請求 進來,首先調用的是 Server.ServeHTTP(rw ResponseWriter, req *Request).
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { ..... h, _ := mux.Handler(r) h.ServeHTTP(w, r)}
上面的函數 再經過倒騰,最後轉到這到ServeMux#handler函數裡面:
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進行了匹配。匹配上就返回對應的URL的handle,否則就是 調用 NotFoundHandler。然後調用muxEntry.h,就是我們自訂處理的邏輯函數。
這樣一個完整的 請求 http請求在golang裡面的流通過程已經非常的清晰了是不是 ?
關注程式猿公眾帳號: