net/http包也是net包中的咳短,是在net tcp協(xié)議基礎(chǔ)上實(shí)現(xiàn)的http協(xié)議。底層調(diào)用的方法也是基于net dial里面的那幾個(gè)方法砰粹,當(dāng)然不止于這些唧躲,下面大致從http服務(wù)器創(chuàng)建開始說起。
一碱璃、創(chuàng)建http服務(wù)
先簡(jiǎn)單看下代碼:
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, request *http.Request) {
fmt.Println("Hello World.")
fmt.Fprintf(w, "Hello World.\n")
})
err := http.ListenAndServe("0.0.0.0:8899", nil)
if err != nil {
fmt.Println("http listen failed.")
}
}
簡(jiǎn)單幾行代碼就搭建了一個(gè)http服務(wù)弄痹,在瀏覽器通過http://127.0.0.1:8899 訪問一下就可以看到結(jié)果。是不是很簡(jiǎn)單厘贼,只需要兩個(gè)方法界酒,一個(gè)ListenAndServe 方法,它的作用就是啟動(dòng)服務(wù)端監(jiān)聽端口嘴秸,監(jiān)聽8899端口過來的請(qǐng)求毁欣,請(qǐng)求過來以后怎么處理,就要看HandleFunc方法岳掐,這個(gè)是對(duì)請(qǐng)求過來后訪問的路徑綁定處理函數(shù)凭疮。為了簡(jiǎn)單就只有一個(gè)路徑,如果想添加多個(gè)訪問uri串述,就在多加幾個(gè)HandleFunc即可执解。
HandleFunc 里面?zhèn)鬟f了兩個(gè)參數(shù)w http.ResponseWriter, request *http.Request
這兩個(gè)是什么,我們看下它們的介紹
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
type Request struct {
Method string
URL *url.URL
Proto string // "HTTP/1.0"
ProtoMajor int // 1
ProtoMinor int // 0
Header Header
Body io.ReadCloser
GetBody func() (io.ReadCloser, error)
ContentLength int64
TransferEncoding []string
Close bool
Host string
Form url.Values
PostForm url.Values
MultipartForm *multipart.Form
Trailer Header
RemoteAddr string
RequestURI string
TLS *tls.ConnectionState
Cancel <-chan struct{}
Response *Response
ctx context.Context
}
做了下簡(jiǎn)化處理纲酗,畢竟原文注釋太多太長(zhǎng)了衰腌,從官方給出的說明可以看到,一個(gè)是進(jìn)行請(qǐng)求數(shù)據(jù)處理的觅赊,一個(gè)是相應(yīng)數(shù)據(jù)處理的右蕊。ResponseWriter是個(gè)接口,只要實(shí)現(xiàn)了這個(gè)接口就可以吮螺,net/http/server.go 文件里面有對(duì)應(yīng)的實(shí)現(xiàn)方法饶囚,可以自己看下帕翻,而request是個(gè)結(jié)構(gòu)體變量。我們都知道變量需要初始化賦值才可用萝风,但從示例里面似乎沒看到在哪賦值嘀掸,直接HandleFunc 拿來就可以用了,那么賦值這一步操作在哪呢规惰,只有兩個(gè)方法睬塌,一個(gè)是調(diào)用,那么初始化這一步只可能在ListenAndServe方法里了卿拴,我們進(jìn)入ListenAndServe方法里面看看衫仑。
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
這里面沒有,只是初始化了服務(wù)器變量堕花,我們繼續(xù)進(jìn)入 server.ListenAndServe()
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
里面也沒有文狱,為了簡(jiǎn)化只貼出有用的代碼,我們繼續(xù)往下看缘挽。
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
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
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
這里面代碼就比較長(zhǎng)了瞄崇,我們只看有用的幾個(gè)
1、rw, e := l.Accept() //獲取一個(gè)連接請(qǐng)求壕曼,
2苏研、c := srv.newConn(rw) //連接初始化一下
3赚哗、 go c.serve(ctx) //啟動(dòng)goroutine 來處理請(qǐng)求
從上一篇net dial文件分析流部,我們知道獲取一個(gè)連接以后,就可以通過連接來讀取數(shù)據(jù)扰法,又因?yàn)檫@是http服務(wù)轧飞,為了處理多個(gè)連接衅鹿,所以go采用的是IO多路復(fù)用模式 ,用goroutine來處理請(qǐng)求过咬,我們繼續(xù)往下看大渤。
for {
w, err := c.readRequest(ctx)
...
...
serverHandler{c.server}.ServeHTTP(w, w.req)
...
w.cancelCtx()
w.finishRequest()
...
}
原方法太長(zhǎng)做了簡(jiǎn)化處理,具體代碼可自行查看掸绞,我們只看幾個(gè)主要的地方
1泵三、 w, err := c.readRequest(ctx)
2、 serverHandler{c.server}.ServeHTTP(w, w.req)
第一個(gè)方法就是初始化request變量的方法衔掸,在這個(gè)方法里初始化了request變量和response變量
func (c *conn) readRequest(ctx context.Context) (w *response, err error)
req, err := readRequest(c.bufr, keepHostHeader)
func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err error)
這就是整個(gè)初始化request過程烫幕,通過這個(gè)方法,最終得到了Request變量敞映,里面包含http請(qǐng)求的參數(shù)纬霞,如head頭,url驱显,cookie等信息。傳遞到HandleFunc里面,就可以直接拿來使用了埃疫。
說到這里伏恐,還要再說個(gè)地方,我們知道調(diào)用HandleFunc 方法就可以把url請(qǐng)求綁定到處理方法上栓霜,那么綁定以后為什么訪問url的時(shí)候自動(dòng)就會(huì)調(diào)用綁定方法翠桦,就在于上面說的2:serverHandler{c.server}.ServeHTTP(w, w.req) 這里,
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
可以看到只要找到了url對(duì)應(yīng)的綁定方法胳蛮,就會(huì)調(diào)用销凑,從而就實(shí)現(xiàn)了整個(gè)http服務(wù)器流程。type Handler interface {
ServeHTTP(ResponseWriter, Request)
}
在handler接口只要實(shí)現(xiàn)了ServeHTTP就可以自定義屬于自己的http服務(wù)仅炊,我們熟知的Gin框架就是通過重寫ServeHTTP 實(shí)現(xiàn)自己封裝的gin.Context結(jié)構(gòu)來傳遞信息斗幼。
總結(jié):go net/http包內(nèi)部還實(shí)現(xiàn)了很多處理,包括鎖抚垄,goroutine數(shù)量限制很多邏輯蜕窿,代碼結(jié)構(gòu)也封裝的很好,可以多看看學(xué)習(xí)學(xué)習(xí)呆馁。