Go 中,實現一個最簡單的 http server 很是容易,代碼以下:html
package main import ( "fmt" "net/http" ) func IndexHandlers(w http.ResponseWriter, r *http.Request){ fmt.Fprintln(w, "hello, world") } func main (){ http.HandleFunc("/", IndexHandlers) err := http.ListenAndServe("127.0.0.1:8088", nil) if err != nil { fmt.Printf("listen error:[%v]", err.Error()) } }
經過上面這個簡單的例子,來一點一點學習 Go 的 net/http 實現的 web 服務的原理web
理解 HTTP 相關的網絡應用,主要關注兩個地方:客戶端(client)和服務端(server)api
二者的交互主要是 client 的 request 以及 server 的 response,主要就在於如何接受 client 的 request 並向 client 返回 response服務器
接收 request 的過程當中,最重要的莫過於路由(router),即實現一個 Multiplexer(多路複用的路由系統),Multiplexer 路由系統的目的就是爲了找處處理器函數(handler),handler 將對 request 進行處理,同時構建 response網絡
流程以下:併發
Clinet -> Requests -> [Multiplexer(router) -> handler -> Response -> Clinet
理解 Go 中的 http 服務,最重要的就是要理解 Multiplexer 和 handler,下面對 handler 的幾個重要概念進行說明tcp
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
Go 沒有繼承,類多態的方式能夠經過接口實現,所謂接口則是定義了聲明瞭函數的簽名,任何結構只要實現了與接口函數簽名相同的方式,就等同於實現了接口,Go 的 http 服務都是基於 handler 進行處理ide
任何結構體,只要實現了 ServeHTTP() 方法,這個結構就能夠稱之爲 handler 對象,ServeMux 會使用 handler 並調用其 ServeHTTP() 方法處理請求並返回響應函數
Go 中的 Multiplexer 是基於 ServeMux 結構體,DefaultServeMux是ServeMux的一個實例高併發
Go 中既可使用內置的 DefaultServeMux(默認的路由系統),也能夠自定義
ServeMux 結構中最重要的字段是 m,這是一個 map,key是一些 url 模式,value 則是一個 muxEntry 結構體,定義存儲了具體的 url 模式 和 Handler 接口
ServeMux 同時也實現了 Handler 接口,不過 ServeMux 的 ServeHTTP() 方法不是用來處理 request 和 構建 response 的,而是用來找到路由註冊的 hanlder() 函數
ServeMux 的源碼
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool } type muxEntry struct { explicit bool h Handler pattern string }
除了 ServeMux 和 Handler,還有一個 Server 結構體須要瞭解,從 http.ListenAndServe 的源碼能夠看出,它建立了一個 server 對象,並調用 server 對象的 ListenAndServe() 方法
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
查看 Server 的結構體以下:
type Server struct { Addr string Handler Handler ReadTimeout time.Duration WriteTimeout time.Duration TLSConfig *tls.Config MaxHeaderBytes int TLSNextProto map[string]func(*Server, *tls.Conn, Handler) ConnState func(net.Conn, ConnState) ErrorLog *log.Logger disableKeepAlives int32 nextProtoOnce sync.Once nextProtoErr error }
Server 結構存儲了服務器處理請求常見的字段,其中 Handler 字段也保留了 Handler 接口類型,若是 Server 接口沒有提供 Handler 接口類型,那麼會使用內置的 DefaultServeMux(默認的路由系統),後面再作分析
建立一個 http 服務,大體須要經歷兩個過程,首先須要註冊路由,即提供 url 模式 和 handler 函數的映射,其次就是實例化一個 Server 的對象,並開啓對客戶端的監聽
再看 http 服務中的代碼:
//註冊路由 http.HandleFunc("/", indexHandler) //實例化 Server 類型,並開啓對客戶端的監聽 http.ListenAndServe("127.0.0.1:8000", nil) 或者: server := &Server{Addr: addr, Handler: handler} server.ListenAndServe()
net/http 包提供的註冊路由的 api 很簡單,http.HandleFunc 選擇了 DefaultServeMux 做爲 Multiplexer
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
DefaultServeMux 是 ServeMux 結構體的實例,固然 http 包也提供了 NewServeMux() 方法建立一個 ServeMux 實例,默認則建立一個 DefaultServeMux
// 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
DefaultServeMux 的 HandlerFunc(parrern, handler) 方法實際是定義在 ServeMux 下的
// HandleFunc registers the handler function for the given pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) }
HandlerFunc() 是一個函數,同時實現了 Handler 接口的 ServeHTTP() 方法,使用 HandlerFunc() 函數包裝路由定義的 IndexHandlers() 函數,其目的就是爲了讓這個函數也實現 ServeHTTP() 方法,從而實現 Handler 接口,即轉變成一個 handler 處理器(函數)
發生函數類型轉換的源碼:
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
HandlerFunc 函數實現 Handler 接口的源碼
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
最開始寫的例子中
http.HandleFunc("/",Indexhandler)
這樣 IndexHandler() 函數實現了 Handler 接口,接下來,ServeMux 的 Handle() 方法將會對 pattern 和 IndexHandler() 函數作一個 map 映射
Handler() 函數的主要目的在於把 IndexHandler() 函數 和 pattern 模式綁定到 map[string]muxEntry 這樣一個 map
// Handle registers the handler for the given pattern. // If a handler already exists for pattern, Handle panics. func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern " + pattern) } if handler == nil { panic("http: nil handler") } if mux.m[pattern].explicit { panic("http: multiple registrations for " + pattern) } if mux.m == nil { mux.m = make(map[string]muxEntry) } mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} if pattern[0] != '/' { mux.hosts = true } // Helpful behavior: // If pattern is /tree/, insert an implicit permanent redirect for /tree. // It can be overridden by an explicit registration. n := len(pattern) if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { // If pattern contains a host name, strip it and use remaining // path for redirect. path := pattern if pattern[0] != '/' { // In pattern, at least the last character is a '/', so // strings.Index can't be -1. path = pattern[strings.Index(pattern, "/"):] } url := &url.URL{Path: path} mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern} } }
此時,pattern 和 IndexHandler() 的路由註冊完成,接下來就是如何開啓 Server 的監聽,以接收客戶端的請求
註冊好路由以後,啓動 web 服務還須要開啓服務器監聽,http 包中的 ListenAndServe() 方法中能夠看到建立一個 Server 類型對象,並調用了 Server 類型對象的同名方法
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() } // 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. 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)}) }
Server 的 ListenAndServe() 方法中,會初始化監聽地址 Addr,同時調用 Listen() 方法設置監聽,最後將監聽的 TCP 對象傳入 Serve() 方法
// 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. // // For HTTP/2 support, srv.TLSConfig should be initialized to the // provided listener's TLS Config before calling Serve. If // srv.TLSConfig is non-nil and doesn't include the string "h2" in // Config.NextProtos, HTTP/2 support is not enabled. // // Serve always returns a non-nil error. After Shutdown or Close, the // returned error is ErrServerClosed. 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 } srv.trackListener(l, true) defer srv.trackListener(l, false) baseCtx := context.Background() // base is always background, per Issue 16220 ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { rw, e := l.Accept() if e != nil { select { case <-srv.getDoneChan(): return ErrServerClosed default: } 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) } }
監聽開啓以後,一旦有客戶端請求過來,Go 就開啓一個協程處理請求,主要邏輯都在 Serve() 方法中
Serve() 方法比較長,其主要職能就是,建立一個上下文對象,而後調用 Listener 的 Accept() 方法用來獲取鏈接數據並使用 newConn() 方法建立鏈接對象,最後使用 goroutine 協程的方式處理鏈接請求,由於每個鏈接都開啓了一個協程,請求的上下文都不一樣,同時又保證了 Go 的高併發
Serve()方法的源碼:
使用 defer 定義了函數退出時,鏈接關閉相關的處理,而後就是讀取鏈接的網絡數據,並處理讀取完畢時候的狀態,接下來就是調用 serverHandler{c.server}.ServeHTTP(w, w.req) 方法處理請求了,最後就是請求處理完畢的邏輯
// Serve a new connection. func (c *conn) serve(ctx context.Context) { c.remoteAddr = c.rwc.RemoteAddr().String() ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr()) defer func() { if err := recover(); err != nil && err != ErrAbortHandler { const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf) } if !c.hijacked() { c.close() c.setState(c.rwc, StateClosed) } }() if tlsConn, ok := c.rwc.(*tls.Conn); ok { if d := c.server.ReadTimeout; d != 0 { c.rwc.SetReadDeadline(time.Now().Add(d)) } if d := c.server.WriteTimeout; d != 0 { c.rwc.SetWriteDeadline(time.Now().Add(d)) } if err := tlsConn.Handshake(); err != nil { c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err) return } c.tlsState = new(tls.ConnectionState) *c.tlsState = tlsConn.ConnectionState() if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) { if fn := c.server.TLSNextProto[proto]; fn != nil { h := initNPNRequest{tlsConn, serverHandler{c.server}} fn(c.server, tlsConn, h) } return } } // HTTP/1.x from here on. ctx, cancelCtx := context.WithCancel(ctx) c.cancelCtx = cancelCtx defer cancelCtx() c.r = &connReader{conn: c} c.bufr = newBufioReader(c.r) c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10) for { w, err := c.readRequest(ctx) if c.r.remain != c.server.initialReadLimitSize() { // If we read any bytes off the wire, we're active. c.setState(c.rwc, StateActive) } if err != nil { const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n" if err == errTooLarge { // Their HTTP client may or may not be // able to read this if we're // responding to them and hanging up // while they're still writing their // request. Undefined behavior. const publicErr = "431 Request Header Fields Too Large" fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr) c.closeWriteAndWait() return } if isCommonNetReadError(err) { return // don't reply } publicErr := "400 Bad Request" if v, ok := err.(badRequestError); ok { publicErr = publicErr + ": " + string(v) } fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr) return } // Expect 100 Continue support req := w.req if req.expectsContinue() { if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 { // Wrap the Body reader with one that replies on the connection req.Body = &expectContinueReader{readCloser: req.Body, resp: w} } } else if req.Header.get("Expect") != "" { w.sendExpectationFailed() return } c.curReq.Store(w) if requestBodyRemains(req.Body) { registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead) } else { if w.conn.bufr.Buffered() > 0 { w.conn.r.closeNotifyFromPipelinedRequest() } w.conn.r.startBackgroundRead() } // HTTP cannot have multiple simultaneous active requests.[*] // Until the server replies to this request, it can't read another, // so we might as well run the handler in this goroutine. // [*] Not strictly true: HTTP pipelining. We could let them all process // in parallel even if their responses need to be serialized. // But we're not going to implement HTTP pipelining because it // was never deployed in the wild and the answer is HTTP/2. serverHandler{c.server}.ServeHTTP(w, w.req) w.cancelCtx() if c.hijacked() { return } w.finishRequest() if !w.shouldReuseConnection() { if w.requestBodyLimitHit || w.closedRequestBodyEarly() { c.closeWriteAndWait() } return } c.setState(c.rwc, StateIdle) c.curReq.Store((*response)(nil)) if !w.conn.server.doKeepAlives() { // We're in shutdown mode. We might've replied // to the user without "Connection: close" and // they might think they can send another // request, but such is life with HTTP/1.1. return } if d := c.server.idleTimeout(); d != 0 { c.rwc.SetReadDeadline(time.Now().Add(d)) if _, err := c.bufr.Peek(4); err != nil { return } } c.rwc.SetReadDeadline(time.Time{}) } }
serverHandler 是一個重要的結構體類型,它只有一個字段,即 Server 結構體類型,同時 serverHandler 實現了 Handler 接口,並在該接口方法中作了一個重要的事情,初始化 Multiplexer 路由多路複用器,若是 Server 類型沒有指定 handler 類型對象(實現了 Handler 接口的類型),則使用內置的 DefaultServeMux 做爲 Multiplexer,並調用初始化 Handler 類型對象的 ServeHTTP() 方法
// 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) }
這裏 DefaultServeMux 的 ServeHTTP() 方法其實也是定義在 ServeMux 結構體中的,相關代碼以下:
ServeMux 的 ServeHTTP() 方法經過調用其 handler() 方法尋找註冊到路由上的 handler() 處理函數,並調用該函數的 ServeHTTP() 方法,在上面的例子中則是 IndexHander() 函數
ServeMux 的 handler() 方法對 URL 作了簡單的處理,而後調用 handler() 函數,後者會建立一個鎖,同時調用 match() 方法返回一個 handler 和 pattern
在 match() 方法中,ServeMux 的 m 字段是 map[string]muxEntry,後者存儲了 pattern 和 handler 處理器函數,所以經過迭代 m 尋找出註冊路由的 pattern 模式與實際 url 匹配的 handler() 函數並返回
返回的結構一直傳遞到 ServeMux 的 ServerHTTP() 方法,接下來調用 handler() 函數的 ServeHTTP() 方法,即 IndexHandler() 函數,而後把 response 寫到 http.RequestWriter 對象返回給客戶端
IndexHandler() 函數運行結束,即 serverHandler{c.server}.ServeHTTP(w, w.req) 運行結束,接下來就是對請求處理完畢以後,斷開鏈接的相關邏輯
// Find a handler on a handler map given a path string. // Most-specific (longest) pattern wins. 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 } // Check for longest valid match. var n = 0 for k, v := range mux.m { if !pathMatch(k, path) { continue } if h == nil || len(k) > n { n = len(k) h = v.h pattern = v.pattern } } return } func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // CONNECT requests are not canonicalized. if r.Method == "CONNECT" { return mux.handler(r.Host, r.URL.Path) } // All other requests have any port stripped and path cleaned // before passing to mux.handler. host := stripHostPort(r.Host) path := cleanPath(r.URL.Path) if path != r.URL.Path { _, pattern = mux.handler(host, path) url := *r.URL url.Path = path return RedirectHandler(url.String(), StatusMovedPermanently), pattern } return mux.handler(host, r.URL.Path) } // handler is the main implementation of Handler. // The path is known to be in canonical form, except for CONNECT methods. 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 } // 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) }
至此,Go 中一個完整的 http 服務介紹完畢,包括 註冊路由,開啓監聽,處理鏈接,路由處理函數
多數的 web 應用基於 HTTP 協議,客戶端和服務端經過 request 和 response 的方式交互,一個 server 必不可少的兩部分莫過於路由註冊和鏈接處理,Go 經過一個 ServeMux 實現了 Multiplexer 路由多路複用器來管理路由,同時提供一個 Handler 接口提供 ServeHTTP() 方法實現了包裝 handler() 處理器函數,handler() 函數處理 request 並構造 response
ServeMux 和 handler() 處理器函數的鏈接橋樑就是 Handler 接口,ServeMux 的 ServeHTTP() 方法實現了尋找註冊路由的 handler() 處理器函數,並調用該函數的 ServeHTTP() 方法,ServeHTTP() 方法就是真正處理請求和構造響應的地方
參考連接:https://www.cnblogs.com/zhaof/p/8569743.html
ending ~