這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Go對web伺服器的編寫提供了非常好的支援,標準庫中提供了net/http包來方便編寫伺服器。許多教程和書籍在講到用Go編寫web伺服器時都會直接教新手用http包寫一個最簡單的hello world伺服器,例子差不多都會像這樣:
// 這就是用Go實現的一個最簡短的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) // <-今天講的就是這個ListenAndServe是如何工作的}
可以看到,代碼真的非常簡短,只需要幾行,我們今天要分析的是http.ListenAndServe(),看看這裡面到底都做了些什麼。
首先,http.ListenAndServe用到的所有依賴都在Go源碼中的/src/pkg/net/http/server.go檔案中,開啟它會發現這頁代碼非常長,有2000+行,我們Ctrl+F直接找我們感興趣的部分,發現在1770行左右的部分找到了http.ListenAndServe的定義:
func ListenAndServe(addr string, handler Handler) error {// 建立一個Server結構體,調用該結構體的ListenAndServer方法然後返回server := &Server{Addr: addr, Handler: handler}return server.ListenAndServe()}
從這個函數中就可以看出,調用http.ListenAndServe之後真正起作用的是Server結構體LisntenAndServe方法,給http.ListenAndServe傳遞的參數只是用來建立一個Server結構體執行個體,Server結構體的定義如下:
type Server struct {Addr string // 伺服器的IP地址和連接埠資訊Handler Handler // 請求處理函數的路由複用器ReadTimeout time.Duration WriteTimeout time.DurationMaxHeaderBytes int TLSConfig *tls.Config TLSNextProto map[string]func(*Server, *tls.Conn, Handler)ConnState func(net.Conn, ConnState)ErrorLog *log.LoggerdisableKeepAlives int32 }
如果我們不傳具體的參數給http.ListenAndServe,那麼它會自動以":http"(等價於":80")和DefaulServeMux作為參數來建立Server結構體執行個體。
接下來繼續看看Server.ListenAndServe裡面都做了些什麼,1675行左右可以找到定義:
func (srv *Server) ListenAndServe() error {addr := srv.Addrif addr == "" {addr = ":http" // 如果不指定伺服器位址資訊,預設以":http"作為地址資訊}ln, err := net.Listen("tcp", addr) // 這裡建立了一個TCP Listener,之後用於接收用戶端的串連請求if err != nil {return err}return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) // 調用Server.Serve()函數並返回}
可以看到,Server.ListenAndServe中建立了一個伺服器Listener,然後在返回時把它傳給了Server.Serve()方法並調用Server.Serve()。
繼續分析Server.Serve,定義的位置在1690行左右:
func (srv *Server) Serve(l net.Listener) error {defer l.Close()var tempDelay time.Duration // 這個迴圈就是伺服器的主迴圈了,通過傳進來的listener接收來自用戶端的請求並建立串連,// 然後為每一個串連建立routine執行c.serve(),這個c.serve就是具體的服務處理了for {rw, e := l.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 = 0c, err := srv.newConn(rw)if err != nil {continue}c.setState(c.rwc, StateNew) // before Serve can returngo c.serve() // <-這裡為每一個建立的串連建立routine之後進行服務}}
我們可以接著看看這個conn.serve()裡面是怎麼進行服務的,代碼在1090行附近,只看其中的主要部分:
func (c *conn) serve() {origConn := c.rwc // copy it before it's set nil on Close or Hijack// 這裡做了一些延遲釋放和TLS相關的處理...// 前面的部分都可以忽略,這裡才是主要的迴圈for {w, err := c.readRequest() // 讀取用戶端的請求// ...serverHandler{c.server}.ServeHTTP(w, w.req) //這裡對請求進行處理if c.hijacked() {return}w.finishRequest()if w.closeAfterReply {if w.requestBodyLimitHit {c.closeWriteAndWait()}break}c.setState(c.rwc, StateIdle)}}
經過一路的分析,http.ListenAndServe工作的流程就差不多明晰了,我們可以總結成一張流程圖:
如果轉載請註明出處:http://blog.csdn.net/gophers