GO-net/http源碼閱讀

GO-net/http源碼閱讀

net/http處理Http請求的基本流程

基本流程

Http包的三個關鍵類型

  1. 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) //路由具體實現
    }
  2. 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
    • ServeMux結構體有ServeHTTP()方法(知足Handler接口),主要用於間接調用它所保存的muxEntry中保存的Handler處理器的ServeHTTP()方法。
  3. 關注了上面兩個結構體後就產生了一個問題,咱們的請求處理函數並無顯式實現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)
    }
    • 自行定義的處理函數轉換爲Handler類型就是HandlerFunc調用以後的結果,這個類型默認就實現了ServeHTTP這個接口,即咱們調用了HandlerFunc(f),強制類型轉換f成爲HandlerFunc類型,這樣f就擁有了ServeHTTP方法。

HTTP服務器的執行流程

  1. 經過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()
    }
  2. 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)})
    }
    • ln經過斷言轉換爲了net.TCPListener類型,並將轉換後的類型做爲參數轉換爲tcpKeepAliveListener對象,而後將tcpKeepAliveListener對象傳給srv.Serve()函數做爲參數。
  3. 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消息(心跳,爲了保持鏈接不斷開)。
  4. 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)
        }
    }
  5. 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
    }
  6. 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)
        }
    }
  7. 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)
    }
  8. 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
    }
  9. 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}
    }

GO Http執行流程

  • 首先調用Http.HandleFunc

    按順序作了幾件事:

    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

相關文章
相關標籤/搜索