http.ListenAndServe工作原理、DefaultServeMux的結構


這個函數是http里最重要的一個函數,或者說是服務端代碼整合的最終靈魂。
httpListenAndServe(port string, handler Handler)
第一個參數是監聽的端口、第二個參數是根頁面的處理函數,可以為空。

接下來是它做了些什么。
首先看源碼吧還是
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
首先創建了一個server對象(但是我們拿不到它的引用),調用了server的ListenAndServe()方法
這個server對象,代表了一個正在跑的HTTP協議的Server程序。
addr代表它監聽的端口,handler代表它的根頁面使用的handler處理對象

server.ListenAndServe()方法(注意啊,這個是server對象的方法)
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {//這兒是指的這個server是不是被關了
return ErrServerClosed
}
addr := srv.Addr//取出server的監聽端口號
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)//使用端口號監聽端口,監聽端口返回了一個listen對象
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
type tcpKeepAliveListener struct {
*net.TCPListener
}
目前已經監聽了端口了,並且獲得了監聽端口之后返回的listen對象。並通過listen對象
創建了一個tcpKeepAliveListener(tcp長時間監聽的listener)
然后調用serve的Serve方法
func (srv *Server) Serve(l net.Listener) error {
.............................//忽略了一堆判斷的特殊的情況,我們就看下面就好
for {
rw, e := l.Accept()//通過tcplistener(tcp監聽器)接受來自客戶端的請求,並返回一個Conn
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//跳到這兒了,如果e == nil 的話,沒有發生錯誤的情況
c := srv.newConn(rw)//通過listener返回的Conn創建一個http包的Conn
c.setState(c.rwc, StateNew) // 設置狀態碼
go c.serve(ctx)//通過http的Conn創建一個協程來單獨處理這個Conn,ctx是server的信息。在上面定義的
}
}
以下是newConn
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{//通過Server和rwc創建的連接
server: srv,
rwc: rwc,
}
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
return c
}

總:創建Server(代表應用程序),監聽接口,獲取listener、listener接受客戶端發送來的消息並返回一個net包的conn,通過這個conn,serve創建一個http包的conn,並且這個conn內有serve的引用。
然后開啟這個conn的協程。在conn的協程里做所有的處理操作

go c.serve(ctx)所創建的協程,里面做了很多操作,比如request、respose、responseWriter
等對象的創建,信息的整合,等等等等,最重要的是,將本次請求的url對應到Serve的Hander的map中,並調用它。
獲取Conn內部的url去Serve的Hand的Map集合中去匹配。匹配到的就調用那個處理方法,

我們創建協程是在當listener接收到請求之后才創建的協程,而listener之前的操作都在main協程中操作,但是我們如果不請求的話,程序就會卡再listener.Accept()處,所以我們的http.ListenAndServer應該放在程序的最后。所有的准備操作都應該再ListenerAddServe操作之前執行。

接下來我們看一下路由(可以看作是一個map,每次請求都會到這個map找對應的Handler)的結構
以下是我們程序創建的Serve的其中一個字段:handler的map

http包下的DefaultServeMux//默認的Serve路由集合
http包有DefaultServeMux對象,該對象有一個map[url]Entry,Entry內部放着handler,
serve創建的處理請求的協程會去DefaultServeMux中的map里通過url進行匹配,找到對應的handler處理函數。

var DefaultServeMux = &defaultServeMux
//這是它的字段
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry//url及muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {//muxEntry的字段
h Handler,這個Handler就是我們的一個處理函數對象,它被包在了一個muxEntry中,放入ServeMux的map 中
pattern string
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM