實現一個簡單的 http 服務
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
fmt.Fprint(rw, "hello word")
})
log.Fatal(http.ListenAndServe(":9777", nil))
}
原理解析:
在main()函式的第一行,我們通過http.HandleFunc定義了路由為”/“的響應函式,這個響應函式,接受傳來的Request,並對Response做一定的處理即寫入HelloWorld然後直接返回給瀏覽器。然後便可以直接呼叫http.ListenAndServe來監聽本地的9777埠,便可以直接在瀏覽器上看到Hello World。
HandleFunc 的實現
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
DefaultServeMux是http包中的全域性變數,它的原型是ServeMux這個結構體。
type ServeMux struct {
mu sync.RWMutex //保護m
m map[string]muxEntry //URL:Handler對映表
hosts bool
}
type muxEntry struct {
explicit bool
h Handler
pattern string
}
ServerMux 這個結構體的 HandelFunc
方法:
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler)) //又呼叫了mux.Handle
}
而 mux.Handle 就比較簡單了,就是將 func(http.ResponseWriter,*http.Request)轉換為 http.Handler 然後放入mux的map中。而http.Handler是一個介面型別,二者是如何轉換的?不妨看看 mux.HandleFunc
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
上面這個函式,其實就是一個實現了 http.Handler 介面
的型別,該型別底層基礎型別就是 func(ResponseWriter, *Request)
,我們知道在go語言中除了 指標與介面
其他基礎型別也是可以定義方法的,標準庫定義這個一個型別,為的就是將 普通func(ResponseWriter, *Request)
適配到 http.Handler介面
。
ListenAndServe這個方法
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
原始碼中可以看到該方法會將傳入進來的addr引數和handler送給Server這個結構體,從而新建一個server然後呼叫這個server的ListenAndServe方法,處理流程大致如下:
server監聽到有新連結進來,建立一個goroutine來處理新連結
在goroutine中,將請求和響應分別封裝為 http.Request和http.ResponseWriter物件。然後用這兩個物件作為函式引數呼叫 server.Handler.serveHTTP(…), 而server.Handler 即為我們傳入的 http.ServeMux 物件,而http.ServeMux物件的serveHTTP方法,我們都沒有碰過,裡面到底做了什麼?
http.ServeMux物件的serveHTTP方法做的事,其實就是根據 http.Request物件中的URL 在自己的map中查詢對應的Handler(這個又是我們在step1中新增的),然後執行。
繞了一大圈,簡單來說就是 每當有新請求進來,server都會為我們新建一個goroutine,並在其中根據請求URL呼叫 我們在建立server之前新增的 URL:Handler對映表(通過server中的http.Handler欄位混入)中的相應URL的Handler.
net/http包中幾個重要的型別:http.ServeMux
: 建立URL:Handler對映表http.Server
: 執行HTTP Serverhttp.Request
: 封裝客戶端HTTP請求資料http.ResponseWriter
: 用來構造伺服器端HTTP響應資料http.Handler
: URL處理程式必須實現的介面
本作品採用《CC 協議》,轉載必須註明作者和本文連結