這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
為什麼golang做web編程比其他語言並發高:
Go是通過一個函數ListenAndServe來處理這些事情的,這個底層其實這樣處 理的:初始化一個server對象,然後調用了net.Listen("tcp", addr),也就是底層用TCP協議搭建了一個服 務,然後監控我們設定的連接埠。
下面代碼來自Go的http包的源碼,通過下面的代碼我們可以看到整個的http處理過程:
func (srv *Server) Serve(l net.Listener) error { defer l.Close() var tempDelay time.Duration // how long to sleep on accept failure 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 } log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay) time.Sleep(tempDelay) continue } return e } tempDelay = 0 if srv.ReadTimeout != 0 { rw.SetReadDeadline(time.Now().Add(srv.ReadTimeout)) } if srv.WriteTimeout != 0 { rw.SetWriteDeadline(time.Now().Add(srv.WriteTimeout)) } c, err := srv.newConn(rw) if err != nil { continue } go c.serve() } panic("not reached")}
監控之後如何接收用戶端的請求呢?上面代碼執行監控連接埠之後,調用了srv.Serve(net.Listener)函數,這個 函數就是處理接收用戶端的請求資訊。這個函數裡面起了一個for{},首先通過Listener接收請求,其次建立一個 Conn,最後單獨開了一個goroutine,把這個請求的資料當做參數扔給這個conn去服務:go c.serve()。這個就是 高並發體現了,使用者的每一次請求都是在一個新的goroutine去服務,相互不影響。
那麼如何具體分配到相應的函數來處理請求呢?conn首先會解析request:c.readRequest(),然後擷取相應的 handler:handler := c.server.Handler,也就是我們剛才在調用函數ListenAndServe時候的第二個參數, 我們前面例子傳遞的是nil,也就是為空白,那麼預設擷取handler = DefaultServeMux,那麼這個變數用來做什麼 的呢?對,這個變數就是一個路由器,它用來匹配url跳轉到其相應的handle函數,那麼這個我們有設定過嗎?有,我 們調用的代碼裡面第一句不是調用了http.HandleFunc("/", sayhelloName)嘛。這個作用就是註冊了請求/的 路由規則,當請求uri為"/",路由就會轉到函數sayhelloName,DefaultServeMux會調用ServeHTTP方法,這個方法內 部其實就是調用sayhelloName本身,最後通過寫入response的資訊反饋到用戶端。
Go代碼的執行流程 通過對http包的分析之後,現在讓我們來梳理一下整個的代碼執行過程。
首先調用Http.HandleFunc
按順序做了幾件事:
1 調用了DefaultServerMux的HandleFunc
2 調用了DefaultServerMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加對應的handler和路由規則 其次調用http.ListenAndServe(":9090", 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 在這個例子中,下面就進入到DefaultServerMux.ServeHttp
10 根據request選擇handler,並且進入到這個handler的ServeHTTP mux.handler(r).ServeHTTP(w, r)
11 選擇handler:
A 判斷是否有路由能滿足這個request(迴圈遍曆ServerMux的muxEntry)
B 如果有路由滿足,調用這個路由handler的ServeHttp
C 如果沒有路由滿足,調用NotFoundHandler的ServeHttp