1. 程式人生 > >golang Http服務淺析

golang Http服務淺析

golang的HTTP包提供了了很強大的功能,開發人員即使不使用框架也可以很方便的進行開發。下面就簡單說一下開發web應用時HTTP包都做了哪些工作。

我們在建立一個WEB應用的時候經常會這樣使用:

```golang

    http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {         writer.Write([]byte("hello"))     })     http.ListenAndServe(":8080",nil)

```

這樣我們訪問`8080`埠就會列印`hello`。

下面從兩個介紹HTTP的工作流程:  1.handler註冊過程  2.伺服器監聽過程  3.當我們訪問介面時伺服器的處理過程。

####一 註冊過程

註冊的程式碼是: ```golang

    http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) {         writer.Write([]byte("hello"))     })      ```

看一下`http.HandleFunc`函式:

```golang

var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {     DefaultServeMux.HandleFunc(pattern, handler)  //呼叫ServeMux的HandleFunc函式 }

```

這裡發現http.HandleFunc呼叫的是DefaultServeMux的HandleFunc函式,DefaultServeMux是ServeMux型別的變數。下面看看ServeMux。

``` type ServeMux struct {     mu    sync.RWMutex     m     map[string]muxEntry  //key是路徑     hosts bool }

type muxEntry struct {     h       Handler   //通過HandleFunc函式傳過來的handler     pattern string  //路徑 }

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {     mux.Handle(pattern, HandlerFunc(handler))  //呼叫Handle }

func (mux *ServeMux) Handle(pattern string, handler Handler) {     mux.mu.Lock()     defer mux.mu.Unlock()     //....     mux.m[pattern] = muxEntry{h: handler, pattern: pattern}     //.... }

```

這裡HandleFunc函式呼叫Handle函式,然後通過mux.m[pattern] = muxEntry{h: handler, pattern: pattern}把handler儲存到muxEntry然後放到ServeMux的map中。 這裡可以發現HandleFunc函式中會把handler強制轉換為HandlerFunc型別:HandlerFunc(handler)。 來看看HandleFunc型別。

```golang

type HandlerFunc func(ResponseWriter, *Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {     f(w, r) }

``` HandlerFunc和我們傳入的Handler的函式簽名是一樣的,只是它還有一個ServeHTTP函式,這個函式會呼叫我們傳入的handler。可以看出HandlerFunc只是對我們傳入的handler進行了包裝並提供ServeHTTP來呼叫`handler`。

這裡總結一下注冊handler的流程:   1.http.HandlerFunc呼叫ServeMux. HandleFunc方法。   2.ServeMux. HandleFunc首先對我們傳進來的handler強制轉換為HandlerFunc型別(目的是提供ServerHTTP介面來執行我們傳進來的Handler),然後呼叫ServeMux. Handle方法。 3.ServeMux. Handle方法會把path和handler存在ServeMux結構的`m`欄位中(使用map的形式存放)。

####二.監聽過程

呼叫http.ListenAndServe函式,看看它的程式碼

```golang

func ListenAndServe(addr string, handler Handler) error {     server := &Server{Addr: addr, Handler: handler}     return server.ListenAndServe()   //呼叫Server型別的同名方法 }

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)}) }

func (srv *Server) Serve(l net.Listener) error {     //....     for {         rw, e := l.Accept() //等待請求         //...     } }

```

http.ListenAndServe呼叫Server.ListenAndServe方法,該方法首先監聽埠net.Listen("tcp", addr) ,然後呼叫了Server.Serve方法來等待請求的到來。

總結一下:         1.`http.ListenAndServe`建立`Server`型別變數並呼叫`Server.ListenAndServe`方法。         2.`Server.ListenAndServe`方法首先會監聽`TCP`埠並呼叫`Server.Serve`方法來等待和處理請求。         

####三.處理請求 處理請求的邏輯也是在Server.Serve方法中。

```golang

func (srv *Server) Serve(l net.Listener) error {     //....     for {         rw, e := l.Accept() //等待請求         //...         c := srv.newConn(rw)  //這裡建立了一個conn結構體,它代表一個連線         c.setState(c.rwc, StateNew) // before Serve can return         go c.serve(ctx)     } }                  ```

呼叫conn.serve方法:

```golang

func (c *conn) serve(ctx context.Context) {         //...         serverHandler{c.server}.ServeHTTP(w, w.req)     //... }

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {     handler := sh.srv.Handler     if handler == nil {         handler = DefaultServeMux     }     handler.ServeHTTP(rw, req) }

``` conn.serve呼叫serverHandler的ServeHTTP方法,這個方法會呼叫DefaultServeMux(如果我們沒有手動設定新的ServerMux的話)的ServeHTTP方法。

到目前為止,我們已經知道實現ServeHTTP的型別有兩個:ServerMux和HandlerFunc,也就是他們都實現了下面的Handler介面。 ```golang

type Handler interface {     ServeHTTP(ResponseWriter, *Request) }

``` 這樣說來ServeMux和HandlerFunc本質上都是用來相應請求的handler。ServerMux的m欄位儲存了我們註冊的多個HandlerFunc。HandlerFunc作為一個Handler介面的實現是我們自己編寫的用來處理請求的,那麼ServerMux做為Handler介面的實現的實現是用來幹什麼的呢?這裡可以直接給出答案:是用來做路由的。 看一下ServerMux的ServeHTTP函式:

```golang

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {     //...     h, _ := mux.Handler(r)  //獲取對應的handler     h.ServeHTTP(w, r)    //執行handler }

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {     //...     return mux.handler(host, r.URL.Path)   //不繼續深究,有興趣看原始碼,這裡返回的是一個handler } ```

上面程式碼很簡單,mux.handler的實現是呼叫的mux.match函式對path進行匹配並返回handler的,這裡就不深究了。

#####總結:  1.**`Handler`介面:**                  包含`ServeHTTP`方法,實現這個介面得有`ServeMux`和`HandlerFunc`(`conn`也實現了,這裡不介紹)。

 2.**`ServeMux`結構體:**                    1.1 它的`m`欄位用於儲存我們註冊的`handler`                 1.2 它實現了`Handler`介面,它的`ServeHTTP`方法提供了路由功能。

 3.**`HandlerFunc`結構體:**                   它實現了`Handler`介面,我們編寫的`handler`都會被轉換為`HandlerFunc`型別,它的`ServeHTTP`方法會執行我們註冊的`handler`。                   3.**`Server`結構體:**                 監聽指定埠,當請求到達的時候建立一個`conn`物件並執行它的`ServeHTTP`方法。

 4.**`conn`結構體:**                 一個`conn`代表一個連線,它的`ServeHTTP`方法會呼叫`ServeMux`的`ServeHTTP`方法進行路由。