標籤:div golang global plist 存在 duration response 方式 handle
首先,要認識一個貫穿始終的介面http.Handler
type Handler interface { ServeHTTP(ResponseWriter, *Request)}
其中,兩個參數,一個是表示響應的介面,另一個表示請求。具體方法先忽略:
type ResponseWriter interface {}
使用時,這個函數指這定地址和對應的handler
func ListenAndServe(addr string, handler Handler)
再看下http包內的一個重要函數,Handle,可見,傳入的是一個監聽的http path,第二個參數是上述的handler.
func Handle(pattern string, handler Handler)
看一下如何使用的:
使用介面形式的Handle + ListenAndServe
type ImpHandler struct {} func (h ImpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 實現方法 w.Write([]byte("haha"))} func main() { http.Handle("/", ImpHandler{}) http.ListenAndServe(":12345", nil )}這裡,http訊息來了應該是在底層直接調用對應的ServeHTTP。具體是怎麼調到的,一層層來看。首先看下http.Handle做了什麼。
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } 可見,這個Handle函數底層封裝了一個對象,其實是對此對象DefaultServeMux進行調用。這類型如下:
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool // whether any patterns contain hostnames} type muxEntry struct { explicit bool h Handler pattern string}
可見,http的path和對應的處理handler的關係以muxEntry維護在這個預設的hash表m中。http.Handle傳入的兩個參數以hash形式儲存在內部的全域變數DefaultServeMux中。
到此,只是在http業務層面上將相關資訊儲存下,最後在http請求來時的ListenAndServe中,才進行串連的處理。
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}
同樣,ListenAndServe本身只是一個對外介面,內部也有相應對象Server進行封裝。前面說過這個方法是處理串連層面的事,那麼這個server就是tcp server的一個抽象。
另一方面,這裡又傳入了一個handler,這是幹嗎用的?這裡傳的是nil,後面再看。
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)})}
可見,這裡直接就監聽TCP串連了。其中的ln是個Listener介面,代碼這樣寫比較漂亮:
// Multiple goroutines may invoke methods on a Listener simultaneously.type Listener interface { // Accept waits for and returns the next connection to the listener. Accept() (Conn, error) // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. Close() error // Addr returns the listener‘s network address. Addr() Addr} // 這裡實現得比較好,覆蓋了一個Accept方法,在其中加入了keepAlived的選項。其他兩個方法仍舊使用原listener的type tcpKeepAliveListener struct { *net.TCPListener // 外層可直接調它的方法不需要指定成員} func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { tc, err := ln.AcceptTCP() if err != nil { return } tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(3 * time.Minute) return tc, nil}繼續看Server的串連監聽處理:
func (srv *Server) Serve(l net.Listener) error { defer l.Close() if fn := testHookServerServe; fn != nil { fn(srv, l) } var tempDelay time.Duration // how long to sleep on accept failure if err := srv.setupHTTP2_Serve(); err != nil { return err } ////////////////skip for { rw, e := l.Accept() // 取出一個串連,對應accept if e != nil { 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) c.setState(c.rwc, StateNew) // before Serve can return go c.serve(ctx) }}
可見,調用Listener的Accept()後,形成一個抽象的串連,再啟單獨協程去處理它。
協程內讀出對應的資料後,會進行如下調用,此調用將http的業務與底層的tcp串連結合了起來:
serverHandler{c.server}.ServeHTTP(w, w.req)
看下面,最終回調回去了。
// serverHandler delegates to either the server‘s Handler or// DefaultServeMux and also handles "OPTIONS *" requests.type serverHandler struct { srv *Server} func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req)} // 最終回到最開始註冊Handle的地方,進行ServeHTTP的調用 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)}
最終調用到了上文的DefaultServeMux中來。 以上是http一的基礎的結構,下面是一些衍生的用法。
使用HandleFunc + ListenAndServe
func main() { fmt.Println("Hello.") http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request) { w.Write([]byte("haha2")) }) http.ListenAndServe(":12346", nil )}
其中,func可使用閉包也可不用。
看下面代碼:
// HandleFunc registers the handler function for the given pattern.func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler))} // HandleFunc registers the handler function for the given pattern// in the DefaultServeMux.// The documentation for ServeMux explains how patterns are matched.func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}
可見,HandlerFunc只是對Handler的封裝,下面同樣是通過DefaultServeMux來進行。
這裡的重點是以下的寫法,用一個函數來實現某個介面,雖然這介面底層仍然是調用函數本身,這樣就可以直接用函數和之前的介面匹配:
// 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)}
實際上的效果是,明明唯寫了一個函數func(ResponseWriter, *Request),但其他代碼卻可以通過golang的隱式介面方式通過另一個你不知道的函數調用你!這裡,不知道的函數就是ServeHTTP。
Handle掌握了,這裡的HandleFunc就容易了。 更進一步,
ServeMux也是可以使用自訂的值。這時,傳入http.ListenAndServe的第二個參數就是這個mux。
func NewServeMux() *ServeMux { return new(ServeMux) }
這個ServeMux,本身又是隱式實現了Handler。
再次回到這裡,可見最終是調到了ServerMux這裡:
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req)}
總結下:
http包給外面提供了三個層次的介面,每個層次暴露的東西不一樣:
第一層: 只需要關心處理邏輯,直接以HandleFunc實現;
第二層: 以Handle實現,這一層,對外額外暴露了一個Handler介面,需要使用者關注一個ServeHTTP的函數;底層仍然是通過DefaultMux來實現。
第三層: 對外暴露了一個ServeMux,處理請求的方法註冊到這個ServeMux上,將ServeMux傳入。
golang的http分析