<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請(qǐng)求是如何流入gin的呢?

          共 3183字,需瀏覽 7分鐘

           ·

          2021-10-22 12:07

          這篇文章我們主要弄清一個(gè)問題:一個(gè)請(qǐng)求通過 net/http 的 socket 接收到請(qǐng)求后, 是如何回到 gin 中處理邏輯的?

          我們?nèi)匀灰?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)
          ????}
          }

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

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

          net/http ServeHTTP 的作用

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

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

          默認(rèn)的 DefaultServeMux 就實(shí)現(xiàn)了這個(gè) ServeHTTP

          這個(gè) request 的流轉(zhuǎn)過程:

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

          探究 gin ServeHTTP 的調(diào)用鏈路

          下面是 gin 的官方 demo, 僅僅幾行代碼,就啟動(dòng)了一個(gè) 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() 初始化了相關(guān)的參數(shù)
          2. 將路由 /ping 以及對(duì)應(yīng)的 handler 注冊(cè)到路由樹中
          3. 使用 r.Run()?啟動(dòng) 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 客戶端請(qǐng)求的過程與 net/http 沒有差別,會(huì)同樣重復(fù)上面的過程。唯一有差別的位置就是在于獲取 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 的動(dòng)態(tài)轉(zhuǎn)發(fā)特性,最終會(huì)跳轉(zhuǎn)到 gin.Engine.ServeHTTP 函數(shù)中。

          gin.ServeHTTP 的實(shí)現(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 里面拿去一塊內(nèi)存
          2. 對(duì)這塊內(nèi)存做初始化工作,防止數(shù)據(jù)污染
          3. 處理請(qǐng)求 handleHTTPRequest
          4. 請(qǐng)求處理完成后,把這塊內(nèi)存歸還到 sync.pool 中

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

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

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


          ? ?


          喜歡明哥文章的同學(xué)
          歡迎長按下圖訂閱!

          ???

          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  在线免费a∨ | 操逼视频网站在线观看 | 中文字幕+乱码+中文字幕明步 | 午夜在线成人视频 | 久久大香蕉精 |