# 3.3 Go如何使得Web工作 前面小節介紹瞭如何透過Go建立一個Web服務,我們可以看到簡單應用一個net/http套件就方便的建立起來了。那麼Go在底層到底是怎麼做的呢?萬變不離其宗,Go的Web服務工作也離不開我們第一小節介紹的Web工作方式。 ## web工作方式的幾個概念 以下均是伺服器端的幾個概念 Request:使用者請求的資訊,用來解析使用者的請求資訊,包括post、get、cookie、url等資訊 Response:伺服器需要反饋給客戶端的資訊 Conn:使用者的每次請求連結 Handler:處理請求和產生返回資訊的處理邏輯 ## 分析http套件執行機制 下圖是Go實現Web服務的工作模式的流程圖 ![](images/3.3.http.png?raw=true) 圖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處理過程: ```Go 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的資訊反饋到客戶端。 詳細的整個流程如下圖所示: ![](images/3.3.illustrator.png?raw=true) 圖3.10 一個http連線處理流程 至此我們的三個問題已經全部得到了解答,你現在對於Go如何讓Web跑起來的是否已經基本瞭解了呢? ## links * [目錄]() * 上一節: [GO建立一個簡單的web服務](<03.2.md>) * 下一節: [Go的http套件詳解](<03.4.md>)