<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          gin 源碼閱讀之http請求是如何流入gin的?

          共 4880字,需瀏覽 10分鐘

           ·

          2021-09-27 10:56

          本篇文章是 gin 源碼分析系列的第二篇,這篇文章我們主要弄清一個問題:一個請求通過 net/http 的 socket 接收到請求后, 是如何回到 gin 中處理邏輯的?

          我們仍然以 net/http 的例子開始

          func main() {
              http.HandleFunc("/"func(w http.ResponseWriter, r *http.Request) {
                  w.Write([]byte("Hello World"))
              })

              if err := http.ListenAndServe(":8000"nil); err != nil {
                  fmt.Println("start http server fail:", err)
              }
          }

          這個例子中 http.HandleFunc 通過看源碼,可以看到 URI "/" 被注冊到了 DefaultServeMux 上。

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

          net/http ServeHTTP 的作用

          net/http 里面有個非常重要的 Handler interface。只有實現(xiàn)了這個方法才能請求的處理邏輯引入自己的處理流程中。

          // https://github.com/golang/go/blob/master/src/net/http/server.go#L86-L88
          type Handler interface {
             ServeHTTP(ResponseWriter, *Request)
          }

          默認的 DefaultServeMux 就實現(xiàn)了這個 ServeHTTP

          這個 request 的流轉過程:

          1. socket.accept 接收到客戶端請求后,啟動 go c.serve(connCtx) [net/http server.go:L3013]行,專門處理這次請求,server 繼續(xù)等待客戶端連接
          2. 獲取能處理這次請求的 handler -> serverHandler{c.server}.ServeHTTP(w, w.req) [net/http server.go:L1952]
          3. 跳轉到真正的 ServeHTTP 去匹配路由,獲取 handler
          4. 由于并沒有自定義路由,于是使用的是 net/http 默認路由 [net/http server.go:L2880-2887]
          5. 所以最終調用去 DefaultServeMux 匹配路由,輸出返回對應的結果

          探究 gin ServeHTTP 的調用鏈路

          下面是 gin 的官方 demo, 僅僅幾行代碼,就啟動了一個 echo server。

          package main

          import "github.com/gin-gonic/gin"

          func main() {
              r := gin.Default()
              r.GET("/ping"func(c *gin.Context) {
                  c.JSON(200, gin.H{
                      "message""pong",
                  })
              })
              r.Run() // listen and serve on 0.0.0.0:8080
          }

          這段代碼的大概流程:

          1. r := gin.Default() 初始化了相關的參數(shù)
          2. 將路由 /ping 以及對應的 handler 注冊到路由樹中
          3. 使用 r.Run() 啟動 server

          r.Run 的底層依然是 http.ListenAndServe

          func (engine *Engine) Run(addr ...string) (err error) {
              defer func() { debugPrintError(err) }()

              trustedCIDRs, err := engine.prepareTrustedCIDRs()
              if err != nil {
                  return err
              }
              engine.trustedCIDRs = trustedCIDRs
              address := resolveAddress(addr)
              debugPrint("Listening and serving HTTP on %s\n", address)
              err = http.ListenAndServe(address, engine)
              return
          }

          所以 gin 建立 socket 的過程,accept 客戶端請求的過程與 net/http 沒有差別,會同樣重復上面的過程。唯一有差別的位置就是在于獲取 ServeHTTP 的位置

          func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
              handler := sh.srv.Handler
              if handler == nil {
                  handler = DefaultServeMux
              }
              if req.RequestURI == "*" && req.Method == "OPTIONS" {
                  handler = globalOptionsHandler{}
              }
              handler.ServeHTTP(rw, req)
          }

          由于 sh.srv.Handler 是 interface 類型,但是其真正的類型是 gin.Engine,根據(jù) interace 的動態(tài)轉發(fā)特性,最終會跳轉到 gin.Engine.ServeHTTP 函數(shù)中。

          gin.ServeHTTP 的實現(xiàn)

          func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
              c := engine.pool.Get().(*Context)
              c.writermem.reset(w)
              c.Request = req
              c.reset()

              engine.handleHTTPRequest(c)

              engine.pool.Put(c)
          }

          至此,終于我們看到了 gin.ServeHTTP 的全貌了

          1. 從 sync.pool 里面拿去一塊內存
          2. 對這塊內存做初始化工作,防止數(shù)據(jù)污染
          3. 處理請求 handleHTTPRequest
          4. 請求處理完成后,把這塊內存歸還到 sync.pool 中

          現(xiàn)在看起來這個實現(xiàn)很簡單,其實不然,這才是 gin 能夠處理數(shù)據(jù)的第一步,也僅僅將請求流轉入 gin 的處理流程而已。

          這里做個結論:通過上面的源碼流程分析,我們知道 net/http.ServeHTTP 這個函數(shù)相當重要性, 主要有這個函數(shù)的存在, 才能將請求流轉入目前 Go 的這些框架里面,同學們有興趣的話,可以去看看 echo, iris, go-zero 等框架是如何實現(xiàn) ServeHTTP 的。

          有關 gin 如何匹配路由,獲取 handler 請關注后續(xù)文章。如果你覺得文章還不錯的,歡迎點贊+再看+轉發(fā)。



          推薦閱讀


          福利

          我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關注公眾號 「polarisxu」,回復 ebook 獲??;還可以回復「進群」,和數(shù)萬 Gopher 交流學習。

          瀏覽 73
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91麻豆成人精品国产 | 夜夜夜夜AV | 一级a一级a爱片免费免会永久 | 亚洲天堂精品在线 | 久久久伦理|