golang的http分析

來源:互聯網
上載者:User

標籤: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分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.