Handler接口
:全部請求的處理器、路由ServeMux都知足該接口。緩存
// A Handler responds to an HTTP request. // // ServeHTTP should write reply headers and data to the ResponseWriter // and then return. Returning signals that the request is finished; it // is not valid to use the ResponseWriter or read from the // Request.Body after or concurrently with the completion of the // ServeHTTP call. // // Depending on the HTTP client software, HTTP protocol version, and // any intermediaries between the client and the Go server, it may not // be possible to read from the Request.Body after writing to the // ResponseWriter. Cautious handlers should read the Request.Body // first, and then reply. // // Except for reading the body, handlers should not modify the // provided Request. // // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes // that the effect of the panic was isolated to the active request. // It recovers the panic, logs a stack trace to the server error log, // and either closes the network connection or sends an HTTP/2 // RST_STREAM, depending on the HTTP protocol. To abort a handler so // the client sees an interrupted response but the server doesn't log // an error, panic with the value ErrAbortHandler. type Handler interface { ServeHTTP(ResponseWriter, *Request) //路由具體實現 }
ServeMux結構體
:HTTP請求的多路轉接器(路由),它負責將每個接收到的請求的URL與一個註冊模式的列表進行匹配,並調用和URL最匹配的模式的處理器。它內部用一個map來保存全部處理器Handler。服務器
type ServeMux struct { mu sync.RWMutex //鎖,因爲請求涉及到併發處理,所以這裏須要一個鎖機制 m map[string]muxEntry // 路由規則,一個string對應一個mux實體,這裏的string就是註冊的路由表達式 hosts bool // 是否在任意的規則中帶有host信息 }
其中的muxEntry結構體
類型,則是保存了Handler請求處理器和匹配的模式字符串。併發
type muxEntry struct { h Handler // 這個路由表達式對應哪一個handler pattern string //匹配字符串 }
http包有一個包級別變量DefaultServeMux,表示默認路由:var DefaultServeMux = NewServeMux(),使用包級別的http.Handle()、http.HandleFunc()方法註冊處理器時都是註冊到該路由中。app
// NewServeMux allocates and returns a new ServeMux. func NewServeMux() *ServeMux { return new(ServeMux) } // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = &defaultServeMux var defaultServeMux ServeMux
關注了上面兩個結構體後就產生了一個問題,咱們的請求處理函數並無顯式實現ServeHTTP(ResponseWriter, *Request),它是怎麼能轉換爲Handler類型的對象?這裏就涉及了第三個重要類型,HandlerFunc適配器
:tcp
// The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler that calls f. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
經過http.ListenAndServe(addr string, handler Handler)
啓動服務,經過給定函數構造Server類型對象,而後調用Server對象的ListenAndServer
方法,並將該方法的返回值error返回給調用方。ide
// ListenAndServe listens on the TCP network address addr and then calls // Serve with handler to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. // // The handler is typically nil, in which case the DefaultServeMux is used. // // ListenAndServe always returns a non-nil error. func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
server.ListenAndServe()
內部調用net.Listen("tcp", addr)
,該方法內部又調用net.ListenTCP()
建立並返回一個net.Listener監聽器ln。函數
// ListenAndServe listens on the TCP network address srv.Addr and then // calls Serve to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. // // If srv.Addr is blank, ":http" is used. // // ListenAndServe always returns a non-nil error. After Shutdown or Close, // the returned error is ErrServerClosed. func (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed } 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)}) }
TCPListener
實現了Listener接口,此處tcpKeepAliveListener重寫了Accept()
方法從而實現了Listener接口。高併發
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted // connections. It's used by ListenAndServe and ListenAndServeTLS so // dead TCP connections (e.g. closing laptop mid-download) eventually // go away. type tcpKeepAliveListener struct { *net.TCPListener } func (ln tcpKeepAliveListener) Accept() (net.Conn, error) { tc, err := ln.AcceptTCP() if err != nil { return nil, err } tc.SetKeepAlive(true) //發送心跳 tc.SetKeepAlivePeriod(3 * time.Minute) //設置發送週期 return tc, nil }
Accept()
函數首先調用TCPListener對象的AcceptTCP()函數獲取一個TCP鏈接對象tc,而後tc調用SetKeepAlive(true),讓操做系統爲收到的每個鏈接啓動發送keepalive消息(心跳,爲了保持鏈接不斷開)。func (srv *Server) Serve(l net.Listener) error
函數處理接收到的客戶端的請求信息。這個函數裏有一個for{},首先經過Listener接收請求,其次建立一個Conn,最後單獨開了一個goroutine,把這個請求的數據當作參數扔給這個conn去服務:go c.serve()。這個就是高併發體現了,用戶的每一次請求都是在一個新的goroutine去服務,相互不影響。ui
// Serve accepts incoming connections on the Listener l, creating a // new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. // // HTTP/2 support is only enabled if the Listener returns *tls.Conn // connections and they were configured with "h2" in the TLS // Config.NextProtos. // // Serve always returns a non-nil error and closes l. // After Shutdown or Close, the returned error is ErrServerClosed. func (srv *Server) Serve(l net.Listener) error { if fn := testHookServerServe; fn != nil { fn(srv, l) // call hook with unwrapped listener } l = &onceCloseListener{Listener: l} defer l.Close() if err := srv.setupHTTP2_Serve(); err != nil { return err } if !srv.trackListener(&l, true) { return ErrServerClosed } defer srv.trackListener(&l, false) var tempDelay time.Duration // how long to sleep on accept failure baseCtx := context.Background() // base is always background, per Issue 16220 ctx := context.WithValue(baseCtx, ServerContextKey, srv) //新建一個context來管理每一個鏈接conn的Go程 for { rw, e := l.Accept() //調用tcpKeepAliveListener對象的 Accept() 方法 if e != nil { select { case <-srv.getDoneChan(): return ErrServerClosed //退出Serve方法,並執行延遲調用(從緩存中刪除當前監聽器) default: } //若是發生了net.Error錯誤,則隔一段時間就重試一次,間隔時間每次翻倍,最大爲1秒 if ne, ok := e.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay) time.Sleep(tempDelay) continue } return e } tempDelay = 0 c := srv.newConn(rw) //該方法根據net.Conn、srv構造了一個新的http.conn類型 c.setState(c.rwc, StateNew) // before Serve can return go c.serve(ctx) } }
func (srv *Server) newConn(rwc net.Conn) *conn
建立一個conn對象,若是debugServerConnections爲真,則經過newLoggingConn將rwc中的loggingConn的name信息包裝一下,添加一部分信息。atom
// debugServerConnections controls whether all server connections are wrapped // with a verbose logging wrapper. const debugServerConnections = false // Create new connection from rwc. func (srv *Server) newConn(rwc net.Conn) *conn { c := &conn{ server: srv, rwc: rwc, } if debugServerConnections { c.rwc = newLoggingConn("server", c.rwc) } return c }
func (c *conn) setState(nc net.Conn, state ConnState)
經過傳入一個鏈接和狀態,根據狀態的值改變服務器中該鏈接的追蹤狀況。
func (c *conn) setState(nc net.Conn, state ConnState) { srv := c.server switch state { case StateNew: srv.trackConn(c, true) case StateHijacked, StateClosed: srv.trackConn(c, false) } if state > 0xff || state < 0 { panic("internal error") } packedState := uint64(time.Now().Unix()<<8) | uint64(state) atomic.StoreUint64(&c.curState.atomic, packedState) if hook := srv.ConnState; hook != nil { hook(nc, state) } }
c.serve(ctx)
調用func (c *conn) serve(ctx context.Context)
讀取請求,而後根據conn內保存的server來構造一個serverHandler類型,並調用它的ServeHTTP()方法:serverHandler{c.server}.ServeHTTP(w, w.req)。ServeHTTP
路由器接收到請求以後進行判斷,若是是*那麼關閉連接,否則調用mux.Handler(r)返回對應設置路由的處理Handler,而後執行h.ServeHTTP(w, r)。
// ServeHTTP dispatches the request to the handler whose // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) h.ServeHTTP(w, r) }
h.ServeHTTP(w, r)
調用對應路由的handler的ServerHTTP接口,handler的ServerHTTP接口根據用戶請求的URL和路由器裏面存儲的map去匹配的,當匹配到以後返回存儲的handler,調用這個handler的ServeHTTP接口就能夠執行到相應的函數。
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 }
http包自帶了幾個建立經常使用處理器的函數:FileServer,NotFoundHandler、RedirectHandler、StripPrefix、TimeoutHandler。而RedirectHandler函數就是用來重定向的:它返回一個請求處理器,該處理器會對每一個請求都使用狀態碼code重定向到網址url。
// RedirectHandler returns a request handler that redirects // each request it receives to the given url using the given // status code. // // The provided code should be in the 3xx range and is usually // StatusMovedPermanently, StatusFound or StatusSeeOther. func RedirectHandler(url string, code int) Handler { return &redirectHandler{url, code} }
按順序作了幾件事:
1 調用了DefaultServeMux的HandleFunc
2 調用了DefaultServeMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增長對應的handler和路由規則
其次調用http.ListenAndServe(":9090", nil) - nil使用默認路由器
按順序作了幾件事情:
1 實例化Server
2 調用Server的ListenAndServe()
3 調用net.Listen("tcp", addr)監聽端口
4 啓動一個for循環,在循環體中Accept請求
5 對每一個請求實例化一個Conn,而且開啓一個goroutine爲這個請求進行服務go c.serve()
6 讀取每一個請求的內容w, err := c.readRequest()
7 判斷handler是否爲空,若是沒有設置handler(這個例子就沒有設置handler),handler就設置爲DefaultServeMux
8 調用handler的ServeHttp
9 在這個例子中,下面就進入到DefaultServeMux.ServeHttp
10 根據request選擇handler,而且進入到這個handler的ServeHTTP
mux.handler(r).ServeHTTP(w, r)
11 選擇handler:
A 判斷是否有路由能知足這個request(循環遍歷ServeMux的muxEntry)
B 若是有路由知足,調用這個路由handler的ServeHTTP
C 若是沒有路由知足,調用NotFoundHandler的ServeHTTP