這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
web工作方式的幾個概念
以下均是伺服器端的幾個概念
Request:使用者請求的資訊,用來解析使用者的請求資訊,包括post、get、cookie、url等資訊
Response:伺服器需要反饋給用戶端的資訊
Conn:使用者的每次請求連結
Handler:處理請求和產生返回資訊的處理邏輯
分析http包運行機制
如所示,是Go實現Web服務的工作模式的流程圖
圖3.9 http包執行流程
建立Listen Socket, 監聽指定的連接埠, 等待用戶端請求到來。
Listen Socket接受用戶端的請求, 得到Client Socket, 接下來通過Client Socket與用戶端通訊。
處理用戶端的請求, 首先從Client Socket讀取HTTP請求的協議頭, 如果是POST方法, 還可能要讀取用戶端提交的資料, 然後交給相應的handler處理請求, handler處理完畢準備好用戶端需要的資料, 通過Client Socket寫給用戶端。
這整個的過程裡面我們只要瞭解清楚下面三個問題,也就知道Go是如何讓Web運行起來了
- 如何監聽連接埠?
- 如何接收用戶端請求?
- 如何分配handler?
前面小節的代碼裡面我們可以看到,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 c, err := srv.newConn(rw) if err != nil { continue } go c.serve() }}
監控之後如何接收用戶端的請求呢?上面代碼執行監控連接埠之後,調用了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的資訊反饋到用戶端。
詳細的整個流程如所示:
圖3.10 一個http串連處理流程
至此我們的三個問題已經全部得到瞭解答,你現在對於Go如何讓Web跑起來的是否已經基本瞭解呢?