用GO搭建一個Web伺服器
有人曾和我說過,一門語言應該能夠自己實現一個HTTP服務。PHP做不到,但GO卻輕而易舉。
只需要幾行程式碼,GO就可以實現一個簡單的HTTP服務。
package main
import (
"fmt"
"net/http"
)
func IndexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello world")
}
func main() {
http.HandleFunc("/", IndexHandler)
http.ListenAndServe(":8001", nil)
}
這樣,即可監聽8001埠,並且當有請求訪問的時候完成響應。(這幾乎就完成了nginx的主要功能,不過沒有做負載平衡)
觀察這段程式碼,我們基本能夠從命名大概瞭解每個函式的作用。
http.HandleFunc("/", IndexHandler)
這相當於繫結url的函式,當請求的url是”/”時,那麼將這個請求交給IndexHandler來處理。
http.ListenAndServe(":8001", nil)
顧名思義,監聽8001埠並且啟動服務。
func IndexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello world")
}
處理函式,當有請求的時候輸出hello world。
通過以上分析,GO搭建一個HTTP服務只需用到一個net/http包即可。同時結合上篇文章,要完成http服務需要以下幾點
- Request 使用者請求的資訊,用來解析使用者的請求資訊,包括post、get、cookie、url等資訊
- Response 服務端反饋給客戶端的資訊
- Conn 使用者每次請求的連線
- Handler 處理請求和生產返回資訊的處理邏輯
前3點是一個HTTP服務必須要的結構,第4點是每一個HTTP服務的核心所在。
我們只需要瞭解3個問題,就知道GO是如何將Web服務運作起來。
- 如何監聽介面
- 如何接受客戶端請求
- 如何分配handle
如何監聽介面
GO是通過一個函式 ListenAndServe
做到的。
首先初始化一個server物件,然後呼叫net.Listen("tcp", addr)
底層建立TCP連線,監聽我們設定的埠。
如何接收客戶端請求
這塊的原始碼
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()
}
}
這段程式碼首先起了一個for()
。接收請求Accept()
,若有請求,則建立一個Conn連線,並用協程啟動服務。這時再次進入下一個迴圈,若有請求則新建立一個連線並啟動一個協程服務。若沒有請求,則休息一段時間後再次迴圈。正是利用了go協程特性,使用者的每一個請求都有一個新的goroutine去服務,互不影響,達到了天然支援高併發特性。
如何分配handle
在建立conn之後,conn會首先解析request,c.readRequest()
,然後獲得相應的handle:handler := c.server.Handler
,也就是我們剛才在呼叫函式ListenAndServe
時候的第二個引數,我們前面例子傳遞的是nil,也就是為空,那麼預設獲取handler = DefaultServeMux
,那麼這個變數用來做什麼的呢?對,這個變數就是一個路由器,它用來匹配url跳轉到其相應的handle函式,那麼這個我們有設定過嗎?有,我們呼叫的程式碼裡面第一句不是呼叫了http.HandleFunc("/", IndexHandler)
嘛。這個作用就是註冊了請求/的路由規則,當請求uri為”/”,路由就會轉到函式IndexHandler
,DefaultServeMux
會呼叫ServeHTTP
方法,這個方法內部其實就是呼叫IndexHandler
本身,最後通過寫入response
的資訊反饋到客戶端。