golang Http服務淺析
golang的HTTP包提供了了很強大的功能,開發人員即使不使用框架也可以很方便的進行開發。下面就簡單說一下開發web應用時HTTP包都做了哪些工作。
我們在建立一個WEB應用的時候經常會這樣使用:
```golang
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) { writer.Write([]byte("hello")) }) http.ListenAndServe(":8080",nil)
```
這樣我們訪問`8080`埠就會列印`hello`。
下面從兩個介紹HTTP的工作流程: 1.handler註冊過程 2.伺服器監聽過程 3.當我們訪問介面時伺服器的處理過程。
####一 註冊過程
註冊的程式碼是: ```golang
http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) { writer.Write([]byte("hello")) }) ```
看一下`http.HandleFunc`函式:
```golang
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) //呼叫ServeMux的HandleFunc函式 }
```
這裡發現http.HandleFunc呼叫的是DefaultServeMux的HandleFunc函式,DefaultServeMux是ServeMux型別的變數。下面看看ServeMux。
``` type ServeMux struct { mu sync.RWMutex m map[string]muxEntry //key是路徑 hosts bool }
type muxEntry struct { h Handler //通過HandleFunc函式傳過來的handler pattern string //路徑 }
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) //呼叫Handle }
func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() //.... mux.m[pattern] = muxEntry{h: handler, pattern: pattern} //.... }
```
這裡HandleFunc函式呼叫Handle函式,然後通過mux.m[pattern] = muxEntry{h: handler, pattern: pattern}把handler儲存到muxEntry然後放到ServeMux的map中。 這裡可以發現HandleFunc函式中會把handler強制轉換為HandlerFunc型別:HandlerFunc(handler)。 來看看HandleFunc型別。
```golang
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
``` HandlerFunc和我們傳入的Handler的函式簽名是一樣的,只是它還有一個ServeHTTP函式,這個函式會呼叫我們傳入的handler。可以看出HandlerFunc只是對我們傳入的handler進行了包裝並提供ServeHTTP來呼叫`handler`。
這裡總結一下注冊handler的流程: 1.http.HandlerFunc呼叫ServeMux. HandleFunc方法。 2.ServeMux. HandleFunc首先對我們傳進來的handler強制轉換為HandlerFunc型別(目的是提供ServerHTTP介面來執行我們傳進來的Handler),然後呼叫ServeMux. Handle方法。 3.ServeMux. Handle方法會把path和handler存在ServeMux結構的`m`欄位中(使用map的形式存放)。
####二.監聽過程
呼叫http.ListenAndServe函式,看看它的程式碼
```golang
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() //呼叫Server型別的同名方法 }
func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) //監聽埠 if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) }
func (srv *Server) Serve(l net.Listener) error { //.... for { rw, e := l.Accept() //等待請求 //... } }
```
http.ListenAndServe呼叫Server.ListenAndServe方法,該方法首先監聽埠net.Listen("tcp", addr) ,然後呼叫了Server.Serve方法來等待請求的到來。
總結一下: 1.`http.ListenAndServe`建立`Server`型別變數並呼叫`Server.ListenAndServe`方法。 2.`Server.ListenAndServe`方法首先會監聽`TCP`埠並呼叫`Server.Serve`方法來等待和處理請求。
####三.處理請求 處理請求的邏輯也是在Server.Serve方法中。
```golang
func (srv *Server) Serve(l net.Listener) error { //.... for { rw, e := l.Accept() //等待請求 //... c := srv.newConn(rw) //這裡建立了一個conn結構體,它代表一個連線 c.setState(c.rwc, StateNew) // before Serve can return go c.serve(ctx) } } ```
呼叫conn.serve方法:
```golang
func (c *conn) serve(ctx context.Context) { //... serverHandler{c.server}.ServeHTTP(w, w.req) //... }
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } handler.ServeHTTP(rw, req) }
``` conn.serve呼叫serverHandler的ServeHTTP方法,這個方法會呼叫DefaultServeMux(如果我們沒有手動設定新的ServerMux的話)的ServeHTTP方法。
到目前為止,我們已經知道實現ServeHTTP的型別有兩個:ServerMux和HandlerFunc,也就是他們都實現了下面的Handler介面。 ```golang
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
``` 這樣說來ServeMux和HandlerFunc本質上都是用來相應請求的handler。ServerMux的m欄位儲存了我們註冊的多個HandlerFunc。HandlerFunc作為一個Handler介面的實現是我們自己編寫的用來處理請求的,那麼ServerMux做為Handler介面的實現的實現是用來幹什麼的呢?這裡可以直接給出答案:是用來做路由的。 看一下ServerMux的ServeHTTP函式:
```golang
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { //... h, _ := mux.Handler(r) //獲取對應的handler h.ServeHTTP(w, r) //執行handler }
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { //... return mux.handler(host, r.URL.Path) //不繼續深究,有興趣看原始碼,這裡返回的是一個handler } ```
上面程式碼很簡單,mux.handler的實現是呼叫的mux.match函式對path進行匹配並返回handler的,這裡就不深究了。
#####總結: 1.**`Handler`介面:** 包含`ServeHTTP`方法,實現這個介面得有`ServeMux`和`HandlerFunc`(`conn`也實現了,這裡不介紹)。
2.**`ServeMux`結構體:** 1.1 它的`m`欄位用於儲存我們註冊的`handler` 1.2 它實現了`Handler`介面,它的`ServeHTTP`方法提供了路由功能。
3.**`HandlerFunc`結構體:** 它實現了`Handler`介面,我們編寫的`handler`都會被轉換為`HandlerFunc`型別,它的`ServeHTTP`方法會執行我們註冊的`handler`。 3.**`Server`結構體:** 監聽指定埠,當請求到達的時候建立一個`conn`物件並執行它的`ServeHTTP`方法。
4.**`conn`結構體:** 一個`conn`代表一個連線,它的`ServeHTTP`方法會呼叫`ServeMux`的`ServeHTTP`方法進行路由。