Golang Http Server源碼閱讀

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

 

net.http包裏面有不少文件,都是和http協議相關的,好比設置cookie,header等。其中最重要的一個文件就是server.go了,這裏咱們閱讀的就是這個文件。golang

幾個重要概念

ResponseWriter: 生成Response的接口緩存

Handler: 處理請求和生成返回的接口cookie

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

Conn : 網絡鏈接併發

 

具體分析

幾個接口:

Handler

實現了handler接口的對象就意味着往server端添加了處理請求的邏輯。函數

type Handler interface {

    ServeHTTP(ResponseWriter, *Request)  // 具體的邏輯函數

}

  下面是三個接口(ResponseWriter, Flusher, Hijacker):google

ResponseWriter, Flusher, Hijacker

// ResponseWriter的做用是被Handler調用來組裝返回的Response的
type ResponseWriter interface {
    // 這個方法返回Response返回的Header供讀寫
    Header() Header
 
    // 這個方法寫Response的Body
    Write([]byte) (int, error)
     
    // 這個方法根據HTTP State Code來寫Response的Header
    WriteHeader(int)
}
 
// Flusher的做用是被Handler調用來將寫緩存中的數據推給客戶端
type Flusher interface {
    // 這個方法將寫緩存中數據推送給客戶端
    Flush()
}
 
// Hijacker的做用是被Handler調用來關閉鏈接的
type Hijacker interface {
    // 這個方法讓調用者主動管理鏈接
    Hijack() (net.Conn, *bufio.ReadWriter, error)
 
}

  

response

實現這三個接口的結構是response(這個結構是http包私有的,在文檔中並無顯示,須要去看源碼)spa

// response包含了全部server端的http返回信息
type response struct {
    conn          *conn         // 保存這次HTTP鏈接的信息
    req           *Request // 對應請求信息
    chunking      bool     // 是否使用chunk
    wroteHeader   bool     // header是否已經執行過寫操做
    wroteContinue bool     // 100 Continue response was written
    header        Header   // 返回的http的Header
    written       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() Header
func (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

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。

HandlerFunc(AdminHandler)是一個轉換而非一個函數調用,由於http.HandlerFunc是一個類型。HandlerFunc顯示了在Go語言接口機制中一些不一樣尋常的特色。這是一個有實現了接口http.Handler方法的函數類型。ServeHTTP方法的行爲調用了它自己的函數。所以HandlerFunc是一個讓函數值知足一個接口的適配器(此處是http.Handler接口適配器,由於serverHTTP實現了Handler接口),這裏函數和這個接口僅有的方法有相同的函數簽名。實際上,這個技巧讓一個單一的類型例如MyHandler以多種方式知足http.Handler接口。

附帶上一個play.google寫的一個小例子

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

有興趣繼續研究的同窗能夠繼續試驗下去

下面接着看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獲取Handler
func (mux *ServeMux) handler(r *Request) Handler  //根據Request獲取Handler,內部實現調用match
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) //!!這個說明,ServeHttp也實現了Handler接口,它實際上也是一個Handler!內部實現調用handler
func (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更確切)若是爲空則設置爲DefaultServeMux
    ReadTimeout    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服務,內部調用Serve
func (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服務

  

參考:http://www.cnblogs.com/yjf512/archive/2012/08/22/2650873.html

相關文章
相關標籤/搜索