<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Gin 框架核心架構解析

      Gin 是一個用Go (Golang) 編寫的HTTP Web 框架,架構非常簡潔,得益于Go的net/http庫,不用處理http協議底層細節,可以更好的聚焦于應用層邏輯

      net/http庫概覽

      Gin是基于net/http實現的, 在介紹Gin之前,不妨先了解下net/http

      net/http提供了HTTP客戶端和服務端的功能,這里主要關心服務端的功能

      服務端核心設計

      得益于Go的并發模型,這里并不需要關心像傳統處理網絡請求的ReactorProactor的I/O復用機制,而是為每個請求都創建一個獨立的goroutine來處理(對這部分有興趣可以了解下Go的調度器和goroutine)。

      net/http服務端功能的核心設計是http.Hander接口

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

      任何實現了這個接口的類型都可以作為一個HTTP請求處理器。ServeHTTP 方法接收一個 http.ResponseWriter 和一個 *http.Request,分別用于寫入響應和讀取請求信息。

      這種設計將業務邏輯與底層的網絡細節徹底解耦,開發者只需關注如何處理請求和生成響應即可。

      流程說明

      net/http可以通過調用ListenAndServe監聽端口,啟動HTTP服務

      使用net/http啟動HTTP服務的簡單示例:

      func main() {
          http.Handle("/foo", fooHandler)
      
      	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
      		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
      	})
      
      	log.Fatal(http.ListenAndServe(":8080", nil))
      }
      

      ListenAndServe starts an HTTP server with a given address and handler. The handler is usually nil, which means to use DefaultServeMuxHandle and HandleFunc add handlers to DefaultServeMux

      流程處理示意圖:

      graph TD A[客戶端請求] --> B[net/http.ListenAndServe] B --> C[創建 goroutine] C --> D[解析 HTTP 請求] D --> E[調用 Handler.ServeHTTP] E --> F[業務邏輯處理] F --> G[返回 HTTP 響應]

      http.ListenAndServe

      // ListenAndServe listens on the TCP network address addr and then calls
      // [Serve] with handler to handle requests on incoming connections.
      // Accepted connections are configured to enable TCP keep-alives.
      //
      // The handler is typically nil, in which case [DefaultServeMux] is used.
      //
      // ListenAndServe always returns a non-nil error.
      func ListenAndServe(addr string, handler Handler) error {
          server := &Server{Addr: addr, Handler: handler}
          return server.ListenAndServe()
      }
      

      ListenAndServe的第二個參數接收的就是前面說的實現了http.Handler接口的類型,默認的DefaultServeMux也是實現了該接口的一個類型,Gin的Engine也是一個實現了http.Handler的類型
      DefaultServeMux

      // Handler returns the handler to use for the given request,
      // consulting r.Method, r.Host, and r.URL.Path. It always returns
      // a non-nil handler. If the path is not in its canonical form, the
      // handler will be an internally-generated handler that redirects
      // to the canonical path. If the host contains a port, it is ignored
      // when matching handlers.
      //
      // The path and host are used unchanged for CONNECT requests.
      //
      // Handler also returns the registered pattern that matches the
      // request or, in the case of internally-generated redirects,
      // the path that will match after following the redirect.
      //
      // If there is no registered handler that applies to the request,
      // Handler returns a “page not found” handler and an empty pattern.
      func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
      	if use121 {
      		return mux.mux121.findHandler(r)
      	}
      	h, p, _, _ := mux.findHandler(r)
      	return h, p
      }
      

      總結

      簡單的回顧下,net/http通過ListenAndServe啟動HTTP服務,監聽指定的端口,當有請求到達時,啟動一個goroutine來處理該請求(net/http/server.go),在完成HTTP的解析后,會調用Handler.ServeHTTP方法進行處理,該方法可以通過實現Handler接口來自定義,并在調用ListenAndServe時進行設置。

      Engine: Gin的核心

      上面已經說了,Gin是通過實現http.Handler接口實現的,在Gin中實現這個接口的就是Engine,所以要了解Gin的結構從Engine入手是比較方便的。

      ServeHttp

      net/http在接收到一個請求后,會創建一個goroutine處理該請求,在讀取數據并進行解析后,會調用ServeHttp

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

      ServeHttp處理流程

      graph TD A[請求到來] --> B[從 sync.Pool 獲取 Context] B --> C[重置 Context 狀態] C --> D[處理 HTTP 請求] D --> E[將 Context 歸還 sync.Pool] E --> F[響應返回]

      EngineServeHttp的實現為:

      // ServeHTTP conforms to the http.Handler interface.
      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都會為該請求分配一個gin.Context對象用來管理請求的上下文。為了避免頻繁的創建和銷毀gin.Context,使用了engine.pool來緩存gin.Context對象,每個請求到達時,先從pool中獲取一個gin.Context對象,并重置gin.Context的狀態,之后將gin.Context傳遞下去,請求處理完成后再將gin.Context返回對象池中。

      在獲取到gin.Context之后,通過HandleHttpRequest處理請求,HandleHttpRequest先根據URL確定路由,并獲取綁定到路由路徑上的所有handle,也就是middleWare,這些middleWare組成一個處理鏈,之后依次執行鏈上的handle

      sync.pool 解析

      engine.pool的類型是sync.Pool,是一個線程安全的對象池,提供了Get()Put()方法,可以在多個goroutine中同時使用。
      其內部設計優先考慮本地goroutine緩存以減少鎖競爭(每個goroutine都有一個私有的本地緩存),當 Get() 時會首先從本地緩存獲取,本地沒有再從共享池中獲取,Put() 時也優先放回本地緩存。

      為什么要重置gin.Context?
      sync.Pool不適合存放有狀態且不能被重置的對象。gin.Context就是一個典型的例子,它會存儲請求相關的狀態,例如RequestResponseWriter以及在中間件中傳遞的Keys。如果不重置,下一個請求可能會使用到上一個請求的殘留數據,導致邏輯錯誤。

      Gin通過在ServeHTTP方法中調用c.reset()來解決這個問題。reset方法會將Context的狀態(如RequestResponseWriterKeysindex等)恢復到初始狀態,確保每個請求都能獲得一個干凈的上下文。

      路由和中間件

      Gin的核心是EngineRouterGroup,實際上,Engine嵌入了RouterGroup,也是一個RouterGroup

      // Engine is the framework's instance, it contains the muxer, middleware and configuration settings.
      // Create an instance of Engine, by using New() or Default()
      type Engine struct {
      	RouterGroup
      	...
      }
      

      Gin中的路由通過前綴樹(Radix Tree)進行保存, Engine就是根RouterGroup

      // RouterGroup is used internally to configure router, a RouterGroup is associated with
      // a prefix and an array of handlers (middleware).
      type RouterGroup struct {
      	Handlers HandlersChain
      	basePath string
      	engine   *Engine
      	root     bool
      }
      

      Handlers 存儲了該層級注冊的中間件
      basePath 用于管理路由組的前綴路徑,方便路由組織

      Gin中的路由通過前綴樹(Radix Tree)進行保存,這種數據結構能夠高效地進行動態路由匹配。

      graph TD A[Engine] --> B[RouterGroup /api/v1] A --> C[RouterGroup /admin] B --> D[GET /api/v1/users] B --> E[POST /api/v1/login] C --> F[GET /admin/dashboard] C --> G[POST /admin/settings]

      當你在 GET, POST 等方法中注冊一個路由時,Gin 會執行以下步驟來生成完整的處理鏈:

      1. 獲取父級中間件:首先,它會從當前的 RouterGroup(或 Engine)中獲取其 Handlers 切片。
      2. 拼接處理函數:然后,它會將本次注冊的路由處理函數(handler...)追加到這個切片的末尾。
      3. 存儲到路由樹:最終,這個完整的處理鏈會作為一個整體,與請求方法和路由路徑一起,存儲到 Gin 的路由樹(一個前綴樹 Radix Tree)中。

      路由機制的優勢

      使用Radix Tree作為路由匹配的核心,帶來了以下好處:

      • 高效匹配:能快速定位到匹配的路由,尤其是在路由數量龐大時。
      • 支持動態路由:可以輕松處理/users/:id這類帶有參數的路由。
      • 支持通配符:可以處理/static/*filepath這類通配符路由。

      Context

      gin.Context貫穿整個HTTP請求生命周期,上下文對象除了保存數據用于在不同的中間件之間傳遞數據外,還提供了許多方法方便解析數據和響應數據,以及提供Next()Abort()來控制流程

      傳遞數據

      gin.Context通過一個map[string]any對象來保存數據,并提供了SetGet方法存取其中的數據

      type Context struct {
      	...
      	// Keys is a key/value pair exclusively for the context of the request.
      	// New objects are not accepted.
      	Keys map[string]any
      	...
      }
      

      封裝當前請求和響應

      	c.writermem.reset(w)
      	c.Request = req
      

      上面ServeHTTP的實現中可以看到,會將net/http傳遞過來的http.ResponseWriter*http.Request 保存到gin.Context中。
      gin.Context提供和許多方法方便獲取請求數據和返回響應,而不用直接操作http.ResponseWriter*http.Request

      在讀取數據時,如使用c.ShouldBindJSON(data)獲取數據時,其實現就需要用到*http.Request解析數據:

      // Binding describes the interface which needs to be implemented for binding the
      // data present in the request such as JSON request body, query parameters or
      // the form POST.
      type Binding interface {
      	Name() string
      	Bind(*http.Request, any) error
      }
      // --- binding/json.go
      func (jsonBinding) Bind(req *http.Request, obj any) error {
      	if req == nil || req.Body == nil {
      		return errors.New("invalid request")
      	}
      	return decodeJSON(req.Body, obj)
      }
      

      要返回JSON格式的響應數據,則可以調用c.JSON()將對象格式化為JSON格式。
      Gin使用Render來格式化數據,Render的接口定義為:

      // Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
      type Render interface {
      	// Render writes data with custom ContentType.
      	Render(http.ResponseWriter) error
      	// WriteContentType writes custom ContentType.
      	WriteContentType(w http.ResponseWriter)
      }
      

      其中的Render方法負責將數據格式化并寫到http.ResponseWriter
      JSON的Render:

      // Render (JSON) writes data with custom ContentType.
      func (r JSON) Render(w http.ResponseWriter) error {
      	return WriteJSON(w, r.Data)
      }
      
      // WriteJSON marshals the given interface object and writes it with custom ContentType.
      func WriteJSON(w http.ResponseWriter, obj any) error {
      	writeContentType(w, jsonContentType)
      	jsonBytes, err := json.Marshal(obj)
      	if err != nil {
      		return err
      	}
      	_, err = w.Write(jsonBytes)
      	return err
      }
      

      流程控制

      請求到達后,會根據url進行路由,將匹配到的路由節點中的handlers的函數處理鏈保存到Context中的handlers屬性中:

      type Context struct {
      ...
      	handlers HandlersChain
      	index    int8
      ...
      }
      
      // HandlersChain defines a HandlerFunc slice.
      type HandlersChain []HandlerFunc
      

      Next()方法實際上只是沿著函數處理鏈往下走:

      // Next should be used only inside middleware.
      // It executes the pending handlers in the chain inside the calling handler.
      // See example in GitHub.
      func (c *Context) Next() {
      	c.index++
      	for c.index < int8(len(c.handlers)) {
      		c.handlers[c.index](c)
      		c.index++
      	}
      }
      

      中間件處理流程示意圖:

      graph TD A[請求] --> B[Middleware A] B --> C{調用 c.Next} C --> D[Middleware B] D --> E[調用 c.Next] E --> F[業務處理函數] F --> G[Middleware B] G --> H[Middleware A] H --> I[響應] subgraph "中間件執行順序" direction LR B --> D --> F end subgraph "響應返回順序" direction LR F --> G --> H end

      Abort()index設置為math.MaxInt8 >> 1來終止整個調用鏈:

      // abortIndex represents a typical value used in abort functions.
      const abortIndex int8 = math.MaxInt8 >> 1
      
      // Abort prevents pending handlers from being called. Note that this will not stop the current handler.
      // Let's say you have an authorization middleware that validates that the current request is authorized.
      // If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
      // for this request are not called.
      func (c *Context) Abort() {
      	c.index = abortIndex
      
      

      總結

      Gin框架通過以下核心機制實現了簡潔高效的Web服務:

      • 基于net/http:利用其Handler接口和并發模型,解耦了網絡細節和業務邏輯。
      • Enginesync.Pool:通過Engine作為核心處理器,并使用對象池高效管理gin.Context對象。
      • Radix Tree路由:實現了高性能、支持動態路由的請求匹配。
      • Context對象:貫穿請求生命周期,封裝了請求和響應,提供了流程控制和數據傳遞能力。
      • 中間件機制:通過HandlersChainNext()Abort()實現了靈活的請求處理管道。

      這些設計共同構成了Gin簡潔而強大的架構,使其成為Go語言Web開發中的熱門選擇。


      倉庫地址:lapluma
      微信公眾號:午夜游魚
      image

      posted @ 2025-08-12 23:43  木章永  閱讀(124)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 日韩成人无码影院| 国产精品无码一区二区三区电影| 四虎影视库国产精品一区| 亚洲精品无码成人A片九色播放| 综合久久婷婷综合久久| 久爱www人成免费网站| 国产乱妇乱子视频在播放| 亚洲一区国色天香| 亚洲精品午夜精品| 亚洲日韩中文字幕在线播放| 国产精品久久无码不卡黑寡妇| 制服丝袜中文字幕在线| 亚洲综合视频一区二区三区| 亚洲国产超清无码专区| 亚洲欧美牲交| 日本亚洲欧洲无免费码在线| 三级网站视频在在线播放| 一本久道久久综合中文字幕| 99精品国产成人一区二区| 波多野结衣av无码| 孝昌县| 国产午夜福利大片免费看| 国产永久免费高清在线观看| 少妇粗大进出白浆嘿嘿视频| 美乳丰满人妻无码视频| 伊人久久精品无码麻豆一区| 夜夜偷天天爽夜夜爱| 狂野欧美性猛交免费视频| 中文字幕免费不卡二区| 中文字幕亚洲综合久久青草| 九九热在线免费视频精品| 久久精品第九区免费观看| A级毛片100部免费看| 国产又爽又黄又无遮挡的激情视频| 欧美亚洲h在线一区二区| 亚洲熟妇中文字幕五十路| 丰满大爆乳波霸奶| 91精品人妻中文字幕色| 亚洲精品一区久久久久一品av| 熟女性饥渴一区二区三区| 久久人妻夜夜做天天爽|