http.ListenAndServe()到底做了什么?
參考:https://studygolang.com/articles/25849?fr=sidebar
? http://blog.csdn.net/gophers
實現一個最簡短的hello world服務器
package mainimport "net/http"func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte(`hello world`))})http.ListenAndServe(":3000", nil) }http.ListenAndServe()到底做了什么?
http.ListenAndServe()用到的所有依賴都在Go源碼中的/src/pkg/net/http/server.go文件中,我們可以看到它的定義:
ListenAndServe來監聽TCP網絡地址(srv.Addr),然后調用Serve來處理傳入的請求;
// 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// 如果不指定服務器地址信息,默認以":http"作為地址信息if addr == "" {addr = ":http"}// 創建一個TCP Listener, 用于接收客戶端的連接請求ln, err := net.Listen("tcp", addr)if err != nil {return err}return srv.Serve(ln) }最后調用了Server.Serve()并返回,繼續來看Server.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. // // 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}origListener := ll = &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)baseCtx := context.Background()if srv.BaseContext != nil {baseCtx = srv.BaseContext(origListener)if baseCtx == nil {panic("BaseContext returned a nil context")}}// 接收失敗時,休眠多長時間;休眠時間不斷變長,知道等于time.Second(一千毫秒)var tempDelay time.Duration // how long to sleep on accept failurectx := context.WithValue(baseCtx, ServerContextKey, srv)for {// 等待新的連接建立rw, err := l.Accept()// 處理鏈接失敗if err != nil {select {case <-srv.getDoneChan():return ErrServerCloseddefault:}if ne, ok := err.(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", err, tempDelay)time.Sleep(tempDelay)continue}return err}connCtx := ctxif cc := srv.ConnContext; cc != nil {connCtx = cc(connCtx, rw)if connCtx == nil {panic("ConnContext returned nil")}}tempDelay = 0c := srv.newConn(rw)c.setState(c.rwc, StateNew, runHooks) // before Serve can return// 創建新的協程處理請求go c.serve(connCtx)} }Server.Serve()的整個邏輯大概是:首先創建一個上下文對象,然后調用Listener的Accept()等待新的連接建立;一旦有新的連接建立,則調用Server的newConn()創建新的連接對象 ,并將連接的狀態標志為StateNew,然后開啟一個新的goroutine處理連接請求。繼續看一下conn.Serve()方法:
// 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())// ...// 延遲釋放和TLS相關處理...for {// 循環調用readRequest()方法讀取下一個請求并進行處理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, runHooks)}...... // 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, runHooks)// 將當前請求置為nilc.curReq.Store((*response)(nil))......c.rwc.SetReadDeadline(time.Time{})} }其中最關鍵的一行代碼為serverHandler{c.server}.ServeHTTP(w, w.req),可以繼續看一下serverHandler:
// 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.Handlerif handler == nil {handler = DefaultServeMux}if req.RequestURI == "*" && req.Method == "OPTIONS" {handler = globalOptionsHandler{}}handler.ServeHTTP(rw, req) }這里的sh.srv.Handler就是最初在http.ListenAndServe()中傳入的Handler對象,也就是我們自定義的ServeMux對象。如果該Handler對象為nil,則會使用默認的DefaultServeMux,最后調用ServeMux的ServeHTTP()方法匹配當前路由對應的handler方法。
ServeMux是一個HTTP請求多路復用器,它將每個傳入請求的URL與注冊模式列表進行匹配,并調用與這個URL最匹配的模式的處理程序。
type ServeMux struct {mu sync.RWMutexm map[string]muxEntryes []muxEntry // slice of entries sorted from longest to shortest.hosts bool // whether any patterns contain hostnames }func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {// CONNECT requests are not canonicalized.if r.Method == "CONNECT" {// If r.URL.Path is /tree and its handler is not registered,// the /tree -> /tree/ redirect applies to CONNECT requests// but the path canonicalization does not.if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {return RedirectHandler(u.String(), StatusMovedPermanently), u.Path}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 the given path is /tree and its handler is not registered,// redirect for /tree/.if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {return RedirectHandler(u.String(), StatusMovedPermanently), u.Path}if path != r.URL.Path {_, pattern = mux.handler(host, path)url := *r.URLurl.Path = pathreturn 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 onesif mux.hosts {h, pattern = mux.match(host + path)}if h == nil {h, pattern = mux.match(path)}if h == nil {h, pattern = NotFoundHandler(), ""}return }// 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. mux.es contains all patterns// that end in / sorted from longest to shortest.for _, e := range mux.es {if strings.HasPrefix(path, e.pattern) {return e.h, e.pattern}}return nil, "" }ServeMux的Handler方法就是根據url調用指定handler方法,handler方法的作用是調用match匹配路由。在 match 方法里我們看到之前提到的 map[string]muxEntry和 []muxEntry,在 map[string]muxEntry 中查找是否有對應的路由規則存在;如果沒有匹配的路由規則,則會進行近似匹配。
ServeMux的Handler方法中找到要執行的handler之后,就調用handler的serveHTTP方法。
總結
以上是生活随笔為你收集整理的http.ListenAndServe()到底做了什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【游戏】蜘蛛纸牌的玩法
- 下一篇: 手游问道法宝怎么获得