Files
build-web-application-with-…/zh-tw/03.3.md
2019-02-26 01:40:54 +08:00

4.4 KiB
Raw Blame History

3.3 Go如何使得Web工作

前面小節介紹瞭如何透過Go建立一個Web服務我們可以看到簡單應用一個net/http套件就方便的建立起來了。那麼Go在底層到底是怎麼做的呢萬變不離其宗Go的Web服務工作也離不開我們第一小節介紹的Web工作方式。

web工作方式的幾個概念

以下均是伺服器端的幾個概念

Request使用者請求的資訊用來解析使用者的請求資訊包括post、get、cookie、url等資訊

Response伺服器需要反饋給客戶端的資訊

Conn使用者的每次請求連結

Handler處理請求和產生返回資訊的處理邏輯

分析http套件執行機制

下圖是Go實現Web服務的工作模式的流程圖

圖3.9 http套件執行流程

  1. 建立Listen Socket, 監聽指定的埠, 等待客戶端請求到來。

  2. Listen Socket接受客戶端的請求, 得到Client Socket, 接下來透過Client Socket與客戶端通訊。

  3. 處理客戶端的請求, 首先從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為"/"路由就會轉到函式sayhelloNameDefaultServeMux會呼叫ServeHTTP方法這個方法內部其實就是呼叫sayhelloName本身最後透過寫入response的資訊反饋到客戶端。

詳細的整個流程如下圖所示:

圖3.10 一個http連線處理流程

至此我們的三個問題已經全部得到了解答你現在對於Go如何讓Web跑起來的是否已經基本瞭解了呢