前面小節(jié)介紹了如何通過Go搭建一個(gè)Web服務(wù),我們可以看到簡(jiǎn)單應(yīng)用一個(gè)net/http包就方便的搭建起來了。那么Go在底層到底是怎么做的呢?萬變不離其宗,Go的Web服務(wù)工作也離不開我們第一小節(jié)介紹的Web工作方式。
以下均是服務(wù)器端的幾個(gè)概念
Request:用戶請(qǐng)求的信息,用來解析用戶的請(qǐng)求信息,包括post、get、cookie、url等信息
Response:服務(wù)器需要反饋給客戶端的信息
Conn:用戶的每次請(qǐng)求鏈接
Handler:處理請(qǐng)求和生成返回信息的處理邏輯
如下圖所示,是Go實(shí)現(xiàn)Web服務(wù)的工作模式的流程圖
http://wiki.jikexueyuan.com/project/go-web-programming/images/3.3.http.png" alt="" />
圖3.9 http包執(zhí)行流程
創(chuàng)建Listen Socket, 監(jiān)聽指定的端口, 等待客戶端請(qǐng)求到來。
Listen Socket接受客戶端的請(qǐng)求, 得到Client Socket, 接下來通過Client Socket與客戶端通信。
這整個(gè)的過程里面我們只要了解清楚下面三個(gè)問題,也就知道Go是如何讓W(xué)eb運(yùn)行起來了
前面小節(jié)的代碼里面我們可以看到,Go是通過一個(gè)函數(shù)ListenAndServe來處理這些事情的,這個(gè)底層其實(shí)這樣處理的:初始化一個(gè)server對(duì)象,然后調(diào)用了net.Listen("tcp", addr),也就是底層用TCP協(xié)議搭建了一個(gè)服務(wù),然后監(jiān)控我們?cè)O(shè)置的端口。
下面代碼來自Go的http包的源碼,通過下面的代碼我們可以看到整個(gè)的http處理過程:
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
var tempDelay time.Duration // how long to sleep on accept failure
for {
rw, e := l.Accept()
if e != nil {
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
}
log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
}
}
監(jiān)控之后如何接收客戶端的請(qǐng)求呢?上面代碼執(zhí)行監(jiān)控端口之后,調(diào)用了srv.Serve(net.Listener)函數(shù),這個(gè)函數(shù)就是處理接收客戶端的請(qǐng)求信息。這個(gè)函數(shù)里面起了一個(gè)for{},首先通過Listener接收請(qǐng)求,其次創(chuàng)建一個(gè)Conn,最后單獨(dú)開了一個(gè)goroutine,把這個(gè)請(qǐng)求的數(shù)據(jù)當(dāng)做參數(shù)扔給這個(gè)conn去服務(wù):go c.serve()。這個(gè)就是高并發(fā)體現(xiàn)了,用戶的每一次請(qǐng)求都是在一個(gè)新的goroutine去服務(wù),相互不影響。
那么如何具體分配到相應(yīng)的函數(shù)來處理請(qǐng)求呢?conn首先會(huì)解析request:c.readRequest(),然后獲取相應(yīng)的handler:handler := c.server.Handler,也就是我們剛才在調(diào)用函數(shù)ListenAndServe時(shí)候的第二個(gè)參數(shù),我們前面例子傳遞的是nil,也就是為空,那么默認(rèn)獲取handler = DefaultServeMux,那么這個(gè)變量用來做什么的呢?對(duì),這個(gè)變量就是一個(gè)路由器,它用來匹配url跳轉(zhuǎn)到其相應(yīng)的handle函數(shù),那么這個(gè)我們有設(shè)置過嗎?有,我們調(diào)用的代碼里面第一句不是調(diào)用了http.HandleFunc("/", sayhelloName)嘛。這個(gè)作用就是注冊(cè)了請(qǐng)求/的路由規(guī)則,當(dāng)請(qǐng)求uri為"/",路由就會(huì)轉(zhuǎn)到函數(shù)sayhelloName,DefaultServeMux會(huì)調(diào)用ServeHTTP方法,這個(gè)方法內(nèi)部其實(shí)就是調(diào)用sayhelloName本身,最后通過寫入response的信息反饋到客戶端。
詳細(xì)的整個(gè)流程如下圖所示:
http://wiki.jikexueyuan.com/project/go-web-programming/images/3.3.illustrator.png" alt="" />
圖3.10 一個(gè)http連接處理流程
至此我們的三個(gè)問題已經(jīng)全部得到了解答,你現(xiàn)在對(duì)于Go如何讓W(xué)eb跑起來的是否已經(jīng)基本了解呢?