在Go中使用及其簡單的代碼即可開啟一個web服務闹蒜。如下:
//開啟web服務
func test(){
http.HandleFunc("/", sayHello)
err := http.ListenAndServe(":9090",nil)
if err!=nil {
log.Fatal("ListenAndServer:",err)
}
}
func sayHello(w http.ResponseWriter, r *http.Request){
r.ParseForm()
fmt.Println("path",r.URL.Path)
fmt.Println("scheme",r.URL.Scheme)
fmt.Fprintf(w, "Hello Guest!")
}
在使用ListenAndServe
這個方法時肋僧,系統(tǒng)就會給我們指派一個路由器羞秤,DefaultServeMux
是系統(tǒng)默認使用的路由器来候,如果ListenAndServe
這個方法的第2個參數(shù)傳入nil跷叉,系統(tǒng)就會默認使用DefaultServeMux
。當然营搅,這里也可以傳入自定義的路由器云挟。
先來看http.HandleFunc("/", sayHello)
,從HandleFunc
方法點進去转质,如下:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
在這里調(diào)用了DefaultServeMux
的HandleFunc
方法园欣,這個方法有兩個參數(shù),pattern
是匹配的路由規(guī)則休蟹,handler
表示這個路由規(guī)則對應的處理方法沸枯,并且這個處理方法有兩個參數(shù)。
在我們書寫的代碼示例中赂弓,pattern
對應/
绑榴,handler
對應sayHello
,當我們在瀏覽器中輸入http://localhost:9090
時盈魁,就會觸發(fā)sayHello
方法翔怎。
我們再順著DefaultServeMux
的HandleFunc
方法繼續(xù)點下去,如下:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
在這個方法中杨耙,路由器又調(diào)用了Handle
方法赤套,注意這個Handle
方法的第2個參數(shù),將之前傳入的handler
這個響應方法強制轉(zhuǎn)換成了HandlerFunc
類型珊膜。
這個HandlerFunc
類型到底是個什么呢容握?如下:
type HandlerFunc func(ResponseWriter, *Request)
看來和我們定義的SayHello
方法的類型都差不多。但是3的Nň凇脖旱!
這個HandlerFunc
默認實現(xiàn)了ServeHTTP
接口!這樣HandlerFunc
對象就有了ServeHTTP
方法介蛉!如下:
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
這個細節(jié)是十分重要的萌庆,因為這一步關(guān)乎到當路由規(guī)則匹配時,相應的響應方法是否會被調(diào)用的問題币旧!這個方法的調(diào)用時機會在下一小節(jié)中講到践险。
接下來,我們返回去繼續(xù)看mux
的Handle
方法吹菱,也就是這段代碼mux.Handle(pattern, HandlerFunc(handler))
巍虫。這段代碼做了哪些事呢?源碼如下:
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}
}
}
代碼挺多鳍刷,其實主要就做了一件事占遥,向DefaultServeMux
的map[string]muxEntry
中增加對應的路由規(guī)則和handler
。
map[string]muxEntry
是個什么鬼输瓜?
map
是一個字典對象瓦胎,它保存的是key-value
。
[string]
表示這個字典的key
是string
類型的尤揣,這個key
值會保存我們的路由規(guī)則搔啊。
muxEntry
是一個實例對象,這個對象內(nèi)保存了路由規(guī)則對應的處理方法北戏。
找到相應代碼负芋,如下:
//路由器
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry //路由規(guī)則,一個string對應一個mux實例對象嗜愈,map的key就是注冊的路由表達式(string類型的)
hosts bool // whether any patterns contain hostnames
}
//muxEntry
type muxEntry struct {
explicit bool
h Handler //這個路由表達式對應哪個handler
pattern string
}
//路由響應方法
type Handler interface {
ServeHTTP(ResponseWriter, *Request) //handler的路由實現(xiàn)器
}
ServeMux
就是這個系統(tǒng)默認的路由器旧蛾。
最后,總結(jié)一下這個部分:
1.調(diào)用http.HandleFunc("/", sayHello)
2.調(diào)用DefaultServeMux
的HandleFunc()
蠕嫁,把我們定義的sayHello()
包裝成HandlerFunc
類型
3.繼續(xù)調(diào)用DefaultServeMux
的Handle()
锨天,向DefaultServeMux
的map[string]muxEntry
中增加路由規(guī)則和對應的handler
OK,這部分代碼做的事就這么多拌阴,第一部分結(jié)束。
第二部分主要就是研究這句代碼err := http.ListenAndServe(":9090",nil)
奶镶,也就是ListenAndServe
這個方法迟赃。從這個方法點進去,如下:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
在這個方法中厂镇,初始化了一個server
對象纤壁,然后調(diào)用這個server
對象的ListenAndServe
方法,在這個方法中捺信,如下:
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)})
}
在這個方法中酌媒,調(diào)用了net.Listen("tcp", addr)
欠痴,也就是底層用TCP協(xié)議搭建了一個服務,然后監(jiān)控我們設置的端口秒咨。
代碼的最后喇辽,調(diào)用了srv
的Serve
方法,如下:
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)
ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
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)
}
}
最后3段代碼比較重要雨席,也是Go語言支持高并發(fā)的體現(xiàn)菩咨,如下:
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
上面那一大坨代碼,總體意思是進入方法后陡厘,首先開了一個for
循環(huán)抽米,在for
循環(huán)內(nèi)時刻Accept請求,請求來了之后糙置,會為每個請求創(chuàng)建一個Conn
云茸,然后單獨開啟一個goroutine
,把這個請求的數(shù)據(jù)當做參數(shù)扔給這個Conn
去服務:go c.serve()
谤饭。用戶的每一次請求都是在一個新的goroutine
去服務标捺,每個請求間相互不影響。
在conn
的serve
方法中网持,有一句代碼很重要宜岛,如下:
serverHandler{c.server}.ServeHTTP(w, w.req)
表示serverHandler
也實現(xiàn)了ServeHTTP
接口,ServeHTTP
方法實現(xiàn)如下:
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
為空(這個handler
就可以理解為是我們自定義的路由器)功舀,就會使用系統(tǒng)默認的DefaultServeMux
萍倡,代碼的最后調(diào)用了DefaultServeMux
的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是Handler接口對象
h.ServeHTTP(w, r) //調(diào)用Handler接口對象的ServeHTTP方法實際上就調(diào)用了我們定義的sayHello方法
}
路由器接收到請求之后,如果是*
那么關(guān)閉鏈接辟汰,如果不是*
就調(diào)用mux.Handler(r)
返回該路由對應的處理Handler
列敲,然后執(zhí)行該handler
的ServeHTTP
方法,也就是這句代碼h.ServeHTTP(w, r)
帖汞,mux.Handler(r)
做了什么呢戴而?如下:
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
if r.Method != "CONNECT" {
if p := cleanPath(r.URL.Path); p != r.URL.Path {
_, pattern = mux.handler(r.Host, p)
url := *r.URL
url.Path = p
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
}
return mux.handler(r.Host, r.URL.Path)
}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
var n = 0
for k, v := range mux.m { //mux.m就是系統(tǒng)默認路由的map
if !pathMatch(k, path) {
continue
}
if h == nil || len(k) > n {
n = len(k)
h = v.h
pattern = v.pattern
}
}
return
}
它會根據(jù)用戶請求的URL
到路由器里面存儲的map
中匹配,匹配成功就會返回存儲的handler
翩蘸,調(diào)用這個handler
的ServeHTTP()
就可以執(zhí)行到相應的處理方法了所意,這個處理方法實際上就是我們剛開始定義的sayHello()
,只不過這個sayHello()
被HandlerFunc
又包了一層催首,因為HandlerFunc
實現(xiàn)了ServeHTTP
接口扶踊,所以在調(diào)用HandlerFunc
對象的ServeHTTP()
時,實際上在ServeHTTP ()
的內(nèi)部調(diào)用了我們的sayHello()
郎任。
總結(jié)一下:
1.調(diào)用http.ListenAndServe(":9090",nil)
2.實例化server
3.調(diào)用server
的ListenAndServe()
4.調(diào)用server
的Serve
方法秧耗,開啟for
循環(huán),在循環(huán)中Accept請求
5.對每一個請求實例化一個Conn
舶治,并且開啟一個goroutine
為這個請求進行服務go c.serve()
6.讀取每個請求的內(nèi)容c.readRequest()
7.調(diào)用serverHandler
的ServeHTTP()
分井,如果handler
為空车猬,就把handler
設置為系統(tǒng)默認的路由器DefaultServeMux
8.調(diào)用handler
的ServeHTTP()
=>實際上是調(diào)用了DefaultServeMux
的ServeHTTP()
9.在ServeHTTP()
中會調(diào)用路由對應處理handler
10.在路由對應處理handler
中會執(zhí)行sayHello()
有一個需要注意的點:
DefaultServeMux
和路由對應的處理方法handler
都實現(xiàn)了ServeHTTP
接口,他們倆都有ServeHTTP
方法尺锚,但是方法要達到的目的不同珠闰,在DefaultServeMux
的ServeHttp()
里會執(zhí)行路由對應的處理handler
的ServeHttp()
。