Golang Http Server源碼閱讀

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

這篇文章出現的理由是業務上需要建立一個Web Server。建立web是所有語言出現必須實現的功能之一了。在nginx+fastcgi+php廣為使用的今天,這裡我們不妨使用Go來進行web伺服器的搭建。

前言

使用Go搭建Web伺服器的包有很多,大致有下面幾種方法,直接使用net包,使用net.http包,使用第三方包(比如gorilla)。使用net包就需要從tcp層開始封裝,耗費人力物力極大,果斷捨棄。直接使用封裝好的net.http和第三方包才是上策。這裡我們就選擇了使用官方提供的net.http包來搭建web服務。另外附帶一句,gorilla的第三方包現在使用還是非常廣的,文檔也是比較全的,有興趣的同學可以考慮使用一下。

 

建議看這篇文章前先看一下net/http文檔 http://golang.org/pkg/net/http/

 

net.http包裡面有很多檔案,都是和http協議相關的,比如設定cookie,header等。其中最重要的一個檔案就是server.go了,這裡我們閱讀的就是這個檔案。

幾個重要概念

ResponseWriter: 產生Response的介面

Handler: 處理請求和產生返回的介面

ServeMux: 路由,後面會說到ServeMux也是一種Handler

Conn : 網路連接

 

具體分析

(具體的說明直接以注釋形式放在代碼中)

幾個介面:

Handler

type Handler interface {ServeHTTP(ResponseWriter, *Request)  // 具體的邏輯函數}

實現了handler介面的對象就意味著往server端添加了處理請求的邏輯。

下面是三個介面(ResponseWriter, Flusher, Hijacker):

ResponseWriter, Flusher, Hijacker

// ResponseWriter的作用是被Handler調用來組裝返回的Response的type ResponseWriter interface {// 這個方法返回Response返回的Header供讀寫Header() Header// 這個方法寫Response的BodyWrite([]byte) (int, error)// 這個方法根據HTTP State Code來寫Response的HeaderWriteHeader(int)}// Flusher的作用是被Handler調用來將寫緩衝中的資料推給用戶端type Flusher interface {// 這個方法將寫緩衝中資料推送給用戶端Flush()}// Hijacker的作用是被Handler調用來關閉串連的type Hijacker interface {// 這個方法讓調用者主動管理串連Hijack() (net.Conn, *bufio.ReadWriter, error)}
 

response

實現這三個介面的結構是response(這個結構是http包私人的,在文檔中並沒有顯示,需要去看源碼)

// response包含了所有server端的http返回資訊type response struct {conn          *conn         // 儲存此次HTTP串連的資訊req           *Request // 對應請求資訊chunking      bool     // 是否使用chunkwroteHeader   bool     // header是否已經執行過寫操作wroteContinue bool     // 100 Continue response was writtenheader        Header   // 返回的http的Headerwritten       int64    // Body的位元組數contentLength int64    // Content長度status        int      // HTTP狀態needSniff     bool     // 是否需要使用sniff。(當沒有設定Content-Type的時候,開啟sniff能根據HTTP body來確定Content-Type)closeAfterReply bool     //是否保持長連結。如果用戶端發送的請求中connection有keep-alive,這個欄位就設定為false。requestBodyLimitHit bool //是否requestBody太大了(當requestBody太大的時候,response是會返回411狀態的,並把串連關閉) }

 

在response中是可以看到

func (w *response) Header() Headerfunc (w *response) WriteHeader(code int)func (w *response) Write(data []byte) (n int, err error)func (w *response) Flush()func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error)

這麼幾個方法。所以說response實現了ResponseWriter,Flusher,Hijacker這三個介面

 

HandlerFunc

handlerFunc是經常使用到的一個type

// 這裡將HandlerFunc定義為一個函數類型,因此以後當調用a = HandlerFunc(f)之後, 調用a的ServeHttp實際上就是調用f的對應方法type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)}

 

這裡需要多回味一下了,這個HandlerFunc定義和ServeHTTP合起來是說明了什嗎?說明HandlerFunc的所有執行個體是實現了ServeHttp方法的。另,實現了ServeHttp方法就是什嗎?實現了介面Handler!

 

所以你以後會看到很多這樣的句子:

func AdminHandler(w ResponseWriter, r *Request) {    ...}handler := HandlerFunc(AdminHandler)handler.ServeHttp(w,r)

 

請不要訝異,你明明沒有寫ServeHttp,怎麼能調用呢? 實際上調用ServeHttp就是調用AdminHandler。

好吧,理解這個也花了我較長時間,附帶上一個play.google寫的一個小例子

http://play.golang.org/p/nSt_wcjc2u

有興趣繼續研究的同學可以繼續實驗下去

 

如果你理解了HandlerFunc,你對下面兩個句子一定不會訝異了

func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }func NotFoundHandler() Handler { return HandlerFunc(NotFound) }

 

下面接著看Server.go

ServerMux結構

它就是http包中的路由規則器。你可以在ServerMux中註冊你的路由規則,當有請求到來的時候,根據這些路由規則來判斷將請求分發到哪個處理器(Handler)。

它的結構如下:

type ServeMux struct {mu sync.RWMutex   //鎖,由於請求設計到並發處理,因此這裡需要一個鎖機制m  map[string]muxEntry  // 路由規則,一個string對應一個mux實體,這裡的string就是我註冊的路由運算式}
 

下面看一下muxEntry

type muxEntry struct {explicit bool   // 是否精確匹配h        Handler // 這個路由運算式對應哪個handler}
 

看到這兩個結構就應該對請求是如何路由的有思路了:

當一個請求request進來的時候,server會依次根據ServeMux.m中的string(路由運算式)來一個一個匹配,如果找到了可以匹配的muxEntry,就取出muxEntry.h,這是個handler,調用handler中的ServeHTTP(ResponseWriter, *Request)來組裝Response,並返回。

 

ServeMux定義的方法有:

func (mux *ServeMux) match(path string) Handler   //根據path擷取Handlerfunc (mux *ServeMux) handler(r *Request) Handler  //根據Request擷取Handler,內部實現調用matchfunc (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) //!!這個說明,ServeHttp也實現了Handler介面,它實際上也是一個Handler!內部實現調用handlerfunc (mux *ServeMux) Handle(pattern string, handler Handler) //註冊handler方法func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))  //註冊handler方法(直接使用func註冊)

 

在godoc文檔中經常見到的DefaultServeMux是http預設使用的ServeMux

var DefaultServeMux = NewServeMux()

如果我們沒有自訂ServeMux,系統預設使用這個ServeMux。

 

換句話說,http包外層(非ServeMux)中提供的幾個方法:

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {DefaultServeMux.HandleFunc(pattern, handler)}

實際上就是調用ServeMux結構內部對應的方法。

 

Server

下面還剩下一個Server結構

type Server struct {Addr           string        // 監聽的地址和連接埠Handler        Handler       // 所有請求需要調用的Handler(實際上這裡說是ServeMux更確切)如果為空白則設定為DefaultServeMuxReadTimeout    time.Duration // 讀的最大Timeout時間WriteTimeout   time.Duration // 寫的最大Timeout時間MaxHeaderBytes int           // 要求標頭的最大長度TLSConfig      *tls.Config   // 配置TLS}

Server提供的方法有:

func (srv *Server) Serve(l net.Listener) error   //對某個連接埠進行監聽,裡面就是調用for進行accept的處理了func (srv *Server) ListenAndServe() error  //開啟http server服務,內部調用Servefunc (srv *Server) ListenAndServeTLS(certFile, keyFile string) error //開啟https server服務,內部調用Serve

 

當然Http包也直接提供了方法供外部使用,實際上內部就是執行個體化一個Server,然後調用ListenAndServe方法

func ListenAndServe(addr string, handler Handler) error   //開啟Http服務func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error //開啟HTTPs服務

 

具體例子分析

下面根據上面的分析,我們對一個例子我們進行閱讀。這個例子搭建了一個最簡易的Server服務。當調用http://XXXX:12345/hello的時候頁面會返回“hello world”

func HelloServer(w http.ResponseWriter, req *http.Request) {io.WriteString(w, "hello, world!\n")}func main() {http.HandleFunc("/hello", HelloServer)err := http.ListenAndServe(":12345", nil)if err != nil {log.Fatal("ListenAndServe: ", err)}}

 

首先調用Http.HandleFunc

按順序做了幾件事:

1 調用了DefaultServerMux的HandleFunc

2 調用了DefaultServerMux的Handle

3 往DefaultServeMux的map[string]muxEntry中增加對應的handler和路由規則

 

其次調用http.ListenAndServe(":12345", nil)

按順序做了幾件事情:

1 執行個體化Server

2 調用Server的ListenAndServe()

3 調用net.Listen("tcp", addr)監聽連接埠

4 啟動一個for迴圈,在迴圈體中Accept請求

5 對每個請求執行個體化一個Conn,並且開啟一個goroutine為這個請求進行服務go c.serve()

6 讀取每個請求的內容w, err := c.readRequest()

7 判斷header是否為空白,如果沒有設定handler(這個例子就沒有設定handler),handler就設定為DefaultServeMux

8 調用handler的ServeHttp

9 在這個例子中,下面就進入到DefaultServerMux.ServeHttp

10 根據request選擇handler,並且進入到這個handler的ServeHTTP

       mux.handler(r).ServeHTTP(w, r)

11 選擇handler:

    A 判斷是否有路由能滿足這個request(迴圈遍曆ServerMux的muxEntry)

    B 如果有路由滿足,調用這個路由handler的ServeHttp

    C 如果沒有路由滿足,調用NotFoundHandler的ServeHttp

後記

對於net.http包中server的理解是非常重要的。理清serverMux, responseWriter, Handler, HandlerFunc等常用結構和函數是使用go web的重要一步。個人感覺由於go中文檔較少,像這樣有點複雜的包,看godoc的效果就遠不如直接看代碼來的快和清晰了。實際上在理解了http包後,才會對godoc中出現的句子有所理解。後續還會寫一些文章關於使用net.http構建web server的。請期待之。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.