Go標準包-http包server

bytecc發表於2021-07-18

只需要幾行程式碼就可以啟動http服務

func main()  {
    http.HandleFunc("/", sayHelloWorld) //路由處理, sayHelloWorld是函式變數, handler func(ResponseWriter, *Request)
    err := http.ListenAndServe(":9091", nil) //監聽9091埠
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

1.路由處理-ServeMux

type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    es    []muxEntry // slice of entries sorted from longest to shortest.
    hosts bool       // whether any patterns contain hostnames
}
http包會有個全域性ServeMux,也就是DefaultServeMux,所以一般不用我們例項它
1.1註冊路由
type HandlerFunc func(ResponseWriter, *Request) //定義了函式類,這個類的變數都實現了介面http.Handler

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

func (mux *ServeMux) Handle(pattern string, handler Handler) {    
    e := muxEntry{h: handler, pattern: pattern}
    mux.m[pattern] = e    //實際註冊到ServeMux.m屬性中
}

2.服務物件-Serve

type Server struct {    
    Addr string
    Handler Handler // handler to invoke, http.DefaultServeMux if nil

    TLSConfig *tls.Config
    ReadTimeout time.Duration    
    ReadHeaderTimeout time.Duration    
    WriteTimeout time.Duration
    IdleTimeout time.Duration    
    MaxHeaderBytes int
    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)

    ConnState func(net.Conn, ConnState)    
    ErrorLog *log.Logger
    BaseContext func(net.Listener) context.Context    
    ConnContext func(ctx context.Context, c net.Conn) context.Context
    inShutdown atomicBool // true when when server is in shutdown

    disableKeepAlives int32     // accessed atomically.
    nextProtoOnce     sync.Once // guards setupHTTP2_* init
    nextProtoErr      error     // result of http2.ConfigureServer if used

    mu         sync.Mutex
    listeners  map[*net.Listener]struct{}
    activeConn map[*conn]struct{}
    doneChan   chan struct{}
    onShutdown []func()
}
2.1.監聽埠
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler} //handler一開始是nil
    return server.ListenAndServe()
}

func (srv *Server) ListenAndServe() error {    
    addr := srv.Addr    
    ln, err := net.Listen("tcp", addr) //透過net監聽埠,net.listener
    return srv.Serve(ln)
}

func (srv *Server) Serve(l net.Listener) error {
    for {
        rw, err := l.Accept() //監聽到客戶端有連線,返回net.conn        
        c := srv.newConn(rw) //把net.conn封裝為http.conn
        go c.serve(connCtx) 
    }
}
2.2 連線物件-http.conn
type conn struct {

    server *Server
    cancelCtx context.CancelFunc
    rwc net.Conn
    remoteAddr string
    tlsState *tls.ConnectionState
    werr error

    // r is bufr's read source. It's a wrapper around rwc that provides
    // io.LimitedReader-style limiting (while reading request headers)
    // and functionality to support CloseNotifier. See *connReader docs.
    r *connReader

    // bufr reads from r.
    bufr *bufio.Reader

    // bufw writes to checkConnErrorWriter{c}, which populates werr on error.
    bufw *bufio.Writer

    // lastMethod is the method of the most recent request
    // on this connection, if any.
    lastMethod string

    curReq atomic.Value // of *response (which has a Request in it)

    curState struct{ atomic uint64 } // packed (unixtime<<8|uint8(ConnState))

    // mu guards hijackedv
    mu sync.Mutex

    // hijackedv is whether this connection has been hijacked
    // by a Handler with the Hijacker interface.
    // It is guarded by mu.
    hijackedv bool
}
處理連線
func (c *conn) serve(ctx context.Context) {
    c.remoteAddr = c.rwc.RemoteAddr().String()
    c.r = &connReader{conn: c}
    c.bufr = newBufioReader(c.r)
    c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)

    for {
        w, err := c.readRequest(ctx) //獲取request物件,response物件
        c.curReq.Store(w)

        serverHandler{c.server}.ServeHTTP(w, w.req) //把requset,response交個路由器處理
        w.cancelCtx()        
        c.rwc.SetReadDeadline(time.Time{})
    }
}

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler //一開始listenAndServe時,Handler是nil.
    if handler == nil {
        handler = DefaultServeMux
    }    
    handler.ServeHTTP(rw, req) //最終交給DefaultServeMux處理
}
2.3.路由分發
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    h, _ := mux.Handler(r) //查詢路由獲取對應的HandlerFunc
    h.ServeHTTP(w, r)
}

3.1根據path獲取handler

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

返回http.Handler,註冊的函式都實現了http.Handler介面。

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    host := stripHostPort(r.Host)
    path := cleanPath(r.URL.Path)
    return mux.handler(host, r.URL.Path)
}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {    
    h, pattern = mux.match(path)
    return
}
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    // Check for exact match first.
    v, ok := mux.m[path]
    if ok {
        return v.h, v.pattern
    }
    return nil, ""
}

總結

1.ServeMux 等於srv.Handler,儲存了路由map,實現了ServeHTTP()方法(conn會把w,r傳到這個方法去),很多自定義http服務,都是重寫srv.Handler
2.Serve 監聽埠,管理連線
3.介面Handler 註冊路由的函式必須實現這個介面,為了方便專門定義了一個型別HandlerFunc,只要是HandlerFunc型別的變數,就是實現了介面Handler

type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

4.serverHandler 獨立的結構,為了串聯conn與srv.Handler
在conn連線物件中,接受的請求透過serverHandler{c.server}.ServeHTTP(w, w.req),最終handler.ServeHTTP()

區塊鏈節點使用http包實現json-rpc功能

構造httpServer物件,作為srv.Handler. 連線物件會把w,r傳給httpServer的ServeHTTP方法

func (h *httpServer) start() error {
    h.server = &http.Server{Handler: h} //建立標準庫srv, srv.Handler為httpServer
    listener, err := net.Listen("tcp", h.endpoint)
    h.listener = listener
    go h.server.Serve(listener)
    return nil
}
-------------------------------------------------
func (h *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    rpc := h.httpHandler.Load().(*rpcHandler) //這個是rpc.NewServer()建立的
    rpc.ServeHTTP(w, r)//又轉給rpcHandler
    return
}

這裡其實是多封裝了一層,最終處理的Handler物件是rpc.NewServer(),所有這個rpcHandler等於標準庫的srv.Handler,需要儲存路由map,同時實現ServeHTTP方法。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
用過哪些工具?為啥用這個工具(速度快,支援高併發...)?底層如何實現的?

相關文章