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

90 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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為"/"路由就會轉到函式sayhelloNameDefaultServeMux會呼叫ServeHTTP方法這個方法內部其實就是呼叫sayhelloName本身最後透過寫入response的資訊反饋到客戶端。
詳細的整個流程如下圖所示:
![](images/3.3.illustrator.png?raw=true)
圖3.10 一個http連線處理流程
至此我們的三個問題已經全部得到了解答你現在對於Go如何讓Web跑起來的是否已經基本瞭解了呢
## links
* [目錄](<preface.md>)
* 上一節: [GO建立一個簡單的web服務](<03.2.md>)
* 下一節: [Go的http套件詳解](<03.4.md>)