net/http包源碼追蹤
這次的博客我們將從一個最基本的web服務器開始,逐漸去追蹤並瞭解源代碼的處理過程
一個簡單的web server
package main
import (
"fmt"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World.")
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":3000", nil)
}
代碼很簡單,只提供了根目錄的路由函數,這裏不再細說,下面繼續我們的源代碼閱讀,去探索實現原理
http.HandleFunc源代碼追蹤
|---------------| |--------------------------| |----------|
|http.HandleFunc| ------> |DefaultServeMux.HandleFunc| ------> |mux.Handle|
|---------------| |--------------------------| |----------|
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" {
panic("http: invalid pattern " + pattern)
}
if handler == nil {
panic("http: nil handler")
}
if mux.m[pattern].explicit {
panic("http: multiple registrations for " + pattern)
}
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
if pattern[0] != '/' {
mux.hosts = true
}
// Helpful behavior:
// If pattern is /tree/, insert an implicit permanent redirect for /tree.
// It can be overridden by an explicit registration.
n := len(pattern)
if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
// If pattern contains a host name, strip it and use remaining
// path for redirect.
path := pattern
if pattern[0] != '/' {
// In pattern, at least the last character is a '/', so
// strings.Index can't be -1.
path = pattern[strings.Index(pattern, "/"):]
}
url := &url.URL{Path: path}
mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
}
}
從上面的代碼和追蹤過程我們可以看到調用http.HandFunc的過程,最後一個函數的內容如上,即添加路由,由於內容比較簡單,這裏不再描述,接下來我們來看提到的另外一個函數
http.ListenAndServe源代碼追蹤
|-------------------| |---------------------| |-------------|
|http.ListenAndServe| ------> |server.ListenAndServe| ------> |server.Server|
|-------------------| |---------------------| |-------------|
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
if fn := testHookServerServe; fn != nil {
fn(srv, l)
}
var tempDelay time.Duration // how long to sleep on accept failure
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
srv.trackListener(l, true)
defer srv.trackListener(l, false)
baseCtx := context.Background() // base is always background, per Issue 16220
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
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)
}
}
由上面的代碼我們可以看到,服務器爲每一個請求新建一個連接,同時開一個goroutine來進行邏輯的處理,我們對於源代碼的追蹤就到此爲止,而具體的一個流程就如上面所說的,當有新的請求到來時,並不會阻塞這一過程,也就提供了高併發的處理
serverHandler{c.server}.ServeHTTP(w, w.req)
這裏摘抄Server.serve的一部分,我們繼續追下去
|------------------------|
|serverHandler.ServerHTTP|
|------------------------|
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)
}
我們可以看到,這裏會判斷handler是否爲空,也就是我們傳入ListenAndServe的第二個參數,我們傳入的爲空,因此默認爲DefaultServeMux
,之後再調用handler.ServeHTTP
,我們這裏的DefaultServeMux
就是一個Handler
,因此根據裏面的規則使用就可以了
參考
astaxie/build-web-application-with-golang
這裏強勢安利一下這本書,我個人由於學習了一些golang語法的皮毛,因此直接從web那一章開始看的,3.4小節總的就是上面我描述的過程,在書本之後還有更多詳細的介紹