前面文章我們已經(jīng)實現(xiàn)了一個簡單的Web服務(wù)⊙笄龋現(xiàn)在我們詳細解剖http包边苹,分析內(nèi)部實現(xiàn)的細節(jié)个束。
1.1 http包中重要的類型和接口:
- server:HTTP服務(wù)器茬底,定義監(jiān)聽的地址、端口,處理器等信息。
- conn:用戶每次請求的鏈接谨胞。
- response:返回給用戶的信息胯努。
- request:用戶請求的信息逢防。
- Handler: 處理器灰署,用戶請求到來時溉箕,服務(wù)器的處理邏輯,它是一個包含ServeHTTP方法的接口悦昵。
1.2 http包的運行流程:
2.1 http包如何實現(xiàn)高并發(fā)
http包中肴茄,server每接收一個用戶請求,都會生成一個conn鏈接但指,并生成一個goroutines來處理對應(yīng)的conn寡痰。所以每個請求都是獨立的,相互不阻塞的棋凳。
c := srv.newConn(rw)
c.setState(c.rwc, StateNew)
go c.serve(ctx)
2.2 處理器(Handler)和多路復(fù)用器(ServeMux)
前一小節(jié)我們使用ListenAndServe啟動服務(wù)拦坠,第二個參數(shù)是一個處理器。但是我們傳入的是一個nil剩岳,http是如何工作的呢贞滨?
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
所以當我們的處理器為空時,http包會默認幫我們生成一個DefaultServeMux處理器拍棕。
不是說第二個參數(shù)是處理器嗎疲迂,但是DefaultServeMux是一個ServeMux結(jié)構(gòu)的實例, 是一個多路復(fù)用器。這是怎么回事莫湘?
其實處理器是一個擁有ServeHTTP方法的接口,只要實現(xiàn)了這個方法郑气,它就是一個處理器幅垮。所以DefaultServeMux不僅是一個多路復(fù)用器,而且還是一個處理器尾组。只是這個處理器比較特殊忙芒,它只是根據(jù)請求的URL將請求分配到對應(yīng)的處理器示弓。
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
我們可以自定義自己的處理器
package main
import (
"fmt"
"net/http"
)
// 定一個自定義的Handler的結(jié)構(gòu)
type MyHanlder struct{}
// 為MyHanlder結(jié)構(gòu)實現(xiàn)Hanlder接口的ServeHTTP的方法,此時MyHandler將是一個處理器(Handler)
func (h MyHanlder) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This my handler")
}
func main() {
// 實例化MyHandler
hanlder := MyHanlder{}
//啟動服務(wù)器并監(jiān)聽8000/tcp端口
err := http.ListenAndServe(":8000", hanlder)
if err != nil {
fmt.Println(err)
}
}
2.3 ServeMux和DefaultServeMux
前面我們說DefaultServeMux是一個ServeMux的實例呵萨,它不僅是一個多路復(fù)用器奏属,而且是一個處理器。多路復(fù)用器具有路由功能潮峦,可以根據(jù)請求的URL將請求傳遞給對應(yīng)的處理器處理囱皿。看下http包中默認的多路復(fù)用器是怎么實現(xiàn)的:
type ServeMux struct {
mu sync.RWMutex //鎖忱嘹,應(yīng)為請求是并發(fā)處理的嘱腥,所以這里需要鎖機制
m map[string]muxEntry //路由規(guī)則的map,一個string對應(yīng)一個muxEntry
es []muxEntry
hosts bool
}
type muxEntry struct {
h Handler //string對應(yīng)的處理器
pattern string //匹配的字符串
}
默認的多路復(fù)用器是根據(jù)請求的URL與存儲的map做匹配拘悦,匹配到了就返回對應(yīng)的handler齿兔,然后調(diào)用該handler的ServeHTTP方法來處理請求。
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
if r.Method == "CONNECT" {
if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
return mux.handler(r.Host, r.URL.Path)
}
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
return mux.handler(host, r.URL.Path)
}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
我們也可以根據(jù)接口定義實現(xiàn)自己簡單的路由器
package main
import (
"fmt"
"net/http"
)
// 自定義一個多路復(fù)用器的結(jié)構(gòu)
type MyMux struct{}
// 實現(xiàn)ServeHTTP方法
func (m MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 判斷URL并轉(zhuǎn)到對應(yīng)的handler處理
if r.URL.Path == "/index" {
IndexHandler(w, r)
return
}
http.NotFound(w, r)
return
}
func IndexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "This is index page")
}
func main() {
// 實例化一個自定義的路由器
mux := MyMux{}
err := http.ListenAndServe(":8000", mux)
if err != nil {
fmt.Println(err)
}
}