Golang | http/server Gin框架簡述

http/server

http指的是Golang中的net/http包,這里用的是1.23.10。

概覽

http包的作用文檔里寫的很簡明:Package http provides HTTP client and server implementations.
主要是提供http的客戶端和服務端,也就是能作為客戶端發http請求,也能作為服務端接收http請求。
翻譯一下文檔中服務端的Overview:

ListenAndServe 會啟動一個帶有指定地址和Handler的 HTTP 服務器。Handler通常為 nil,這意味著會使用 DefaultServeMux。Handle 和 HandleFunc 會將處理程序添加到 DefaultServeMux 中:

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

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

通過創建自定義服務器,可以對服務器的行為進行更多控制:

More control over the server’s behavior is available by creating a custom Server:

s := &http.Server{Addr:           ":8080",Handler:        myHandler,ReadTimeout:    10 * time.Second,WriteTimeout:   10 * time.Second,MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())

上面就是全部了,也就是說,ListenAndServe(),這個方法是作為server的核心方法,一切圍繞著這個方法來的,這個方法有兩個參數,一個是地址,另一個是Handler,Handler可以理解為處理器,處理路徑用的。如果不傳入自定義的,就會有默認的DefaultServeMux。

什么是 ServeMux(Multiplexer)?

// DefaultServeMux is the default [ServeMux] used by [Serve].
var DefaultServeMux = &defaultServeMuxvar defaultServeMux ServeMux
type ServeMux struct {mu       sync.RWMutextree     routingNodeindex    routingIndexpatterns []*pattern  // TODO(jba): remove if possiblemux121   serveMux121 // used only when GODEBUG=httpmuxgo121=1
}

有點抽象難懂,簡化版如下,大致的功能是就是一個map,把路徑映射到對應的handler上,也就是處理方法上,在mvc模式里就是controller。

type ServeMux struct {// 極簡版mu    sync.RWMutexm     map[string]muxEntry // 保存 路徑 -> handler 的映射hosts bool                 // 是否根據 host 來區分
}
http.Handle("/foo", fooHandler)   // /foo -> fooHandler
http.HandleFunc("/bar", barFunc)  // /bar -> barFunc

簡單場景夠用,復雜需求再自己 NewServeMux() 或換第三方路由處理器,gin框架就是如此,Gin 用自研的 壓縮前綴樹(Radix Tree) 實現路由,性能比原生的要好。

關于性能:在同樣只測“路由匹配”這一項時,Gin 的 Radix 樹比原生 ServeMux 的 map+線性掃描 快 3~5 倍;如果放到整站 QPS 場景(帶 JSON 編碼、Context 池化等),高并發下 Gin 可以做到原生 mux 的 2~4 倍吞吐量,極限壓測甚至能到 6 倍,但 10 倍以上的說法基本只在“純路由微基準”里出現,生產環境很難復現

另外關于啟動服務的流程:
真正的邏輯應當是把handler和對應的路徑注冊到路由中,然后把路由作為參數放到ListenAndServe中去啟動服務,比較符合開發邏輯,但是go做了簡化。Handle和HandleFunc只是語法糖而已,簡化了把handler方法注冊到默認的ServeMux里面的步驟,調用http.Handle的時候,本質就是本handler方法及對應路徑注冊到ServeMux里,無論是默認還是自定義的。

// 沒有語法糖的世界(繁瑣)
func withoutSugar() {
mux := http.DefaultServeMux// 注冊 handler 對象
mux.Handle("/handler", &myHandler{})// 注冊 handler 函數需要多一步轉換
mux.Handle("/handlerfunc", http.HandlerFunc(myHandlerFunc))http.ListenAndServe(":8080", nil)
}// 有語法糖的世界(簡潔)
func withSugar() {
http.Handle("/handler", &myHandler{})        // 自動處理對象
http.HandleFunc("/handlerfunc", myHandlerFunc) // 自動轉換函數http.ListenAndServe(":8080", nil)
}type myHandler struct{}
func (h *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Handler")
}func myHandlerFunc(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "HandlerFunc")
}

ServeMux

官方說法叫HTTP request multiplexer,http請求多路分發器,但是很書面,中文口語語境可以叫路由。

It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.
它將每個傳入請求的 URL 與已注冊的模式列表進行匹配,并調用與 URL 最匹配的模式對應的處理程序。
這個結構體是路由匹配的實現,先看老版本,比較樸素,使用的map。( Go 1.21+之前)

type ServeMux struct {mu    sync.RWMutexm     map[string]muxEntryes    []muxEntry // slice of entries sorted from longest to shortest.hosts bool       // whether any patterns contain hostnames
}

老版本的ServeMux用的是map結構,很符合直接,一個path直接映射一個handler,但是如果去實現前綴匹配等模式匹配就無法O(1)的映射了,需要去掃表,而前綴匹配又是web開發事實上的剛需,所以是需要實現的,因此為了改進效率放棄了單純的樸素的map實現,改用了樹實現。

前綴匹配:就是路由系統只比較 URL 的開頭部分,來決定由哪個處理函數來接管請求。
例如,我們定義了以下路由:
/api/users -> list_users 函數
/api/users/create-> create_user 函數
/static/... -> 靜態文件處理函數
當一個請求 GET /api/users/123 進來時,框架會從根 / 開始,先匹配到 /api,再匹配到 /api/users。雖然它不完全等于 /api/users/123,但路由系統通常會在匹配了固定前綴后,將剩余部分作為參數(例如 123)傳遞給處理函數,或者交由更具體的路由規則(如 /api/users/)來處理。

關于模式匹配:
文檔原文寫的很清晰,前綴匹配是模式匹配的一類:
Patterns
Patterns can match the method, host and path of a request. Some examples:
“/index.html” matches the path “/index.html” for any host and method.
“GET /static/” matches a GET request whose path begins with “/static/”.
“example.com/” matches any request to the host “example.com”.
“example.com/{$}” matches requests with host “example.com” and path “/”.
“/b/{bucket}/o/{objectname…}” matches paths whose first segment is “b” and whose third segment is “o”. The name “bucket” denotes the second segment and “objectname” denotes the remainder of the path.

go1.21之后的ServeMux結構如下:

type ServeMux struct {mu       sync.RWMutex// 這是核心的結構,核心路由存儲結構,類似 前綴樹(Trie)tree     routingNode//	用于快速定位候選節點,減少匹配樹遍歷// 快速索引 Method + Host + Pathindex    routingIndex // 保存所有注冊過的 pattern,未來可能移除patterns []*pattern  // TODO(jba): remove if possiblemux121   serveMux121 // used only when GODEBUG=httpmuxgo121=1
}

比較關鍵的是tree和index字段,這兩個字段的簡單理解:
tree → 決策樹,用于高效匹配請求 URL 找到 handler
index → 索引表,用于在注冊 pattern 或檢測沖突時快速篩選潛在沖突 pattern

tree的詳情如下:

// A routingNode is a node in the decision tree.
// The same struct is used for leaf and interior nodes.
type routingNode struct {// A leaf node holds a single pattern and the Handler it was registered// with.pattern *patternhandler Handler// An interior node maps parts of the incoming request to child nodes.// special children keys://     "/"	trailing slash (resulting from {$})//	   ""   single wildcardchildren   mapping[string, *routingNode]multiChild *routingNode // child with multi wildcardemptyChild *routingNode // optimization: child with key ""
}
// 用ai翻譯并解釋了一下:
// routingNode 是決策樹中的一個節點。
// 既可作為葉子節點,也可作為中間節點。
// 葉子節點存 handler,中間節點存子節點用于路徑匹配。
type routingNode struct {// 與該節點關聯的路由模式。// 葉子節點:存儲用戶注冊的具體 pattern。// 中間節點:如果此路徑沒有 handler,可能為 nil。pattern *pattern// 與 pattern 對應的處理函數。// 只有葉子節點或注冊了 handler 的節點才非 nil。handler Handler// 子節點映射:key 為路徑的下一段,value 為子節點。// 用于中間節點匹配請求路徑的下一個部分。// 特殊 key://   "/" - 表示結尾斜杠節點(pattern 以 {$} 結尾)//   ""  - 表示單段通配符,匹配任意單路徑片段children   mapping[string, *routingNode]// 多段通配符子節點(pattern 以 '*' 結尾)。// 匹配剩余的任意路徑段。multiChild *routingNode // child with multi wildcard// 空 key 子節點的優化。// 用于快速訪問單段通配符節點,避免每次查 map。emptyChild *routingNode // optimization: child with key ""
}

pattern + handler → 葉子節點信息,用于最終請求處理。
children → 中間節點映射,用于逐段匹配 URL。
multiChild → 多段通配符,匹配剩余路徑段。
emptyChild → 單段通配符優化,減少 map 查找。

下面是index字段對應的源碼:

// A routingIndex optimizes conflict detection by indexing patterns.
//
// The basic idea is to rule out patterns that cannot conflict with a given
// pattern because they have a different literal in a corresponding segment.
// See the comments in [routingIndex.possiblyConflictingPatterns] for more details.
type routingIndex struct {// map from a particular segment position and value to all registered patterns// with that value in that position.// For example, the key {1, "b"} would hold the patterns "/a/b" and "/a/b/c"// but not "/a", "b/a", "/a/c" or "/a/{x}".segments map[routingIndexKey][]*pattern// All patterns that end in a multi wildcard (including trailing slash).// We do not try to be clever about indexing multi patterns, because there// are unlikely to be many of them.multis []*pattern
}// routingIndex 用于優化路由沖突檢測,通過對 pattern 做索引加速查找。
// 基本思路:
// 通過記錄每個路徑片段位置的字面值,快速排除不可能沖突的 pattern。
// 具體細節可參考 possiblyConflictingPatterns 方法的注釋。
type routingIndex struct {// segments 是一個 map:// key 是某個路徑片段的位置和對應的字面值// value 是所有在該位置有相同字面值的已注冊 pattern// 舉例:// key {1, "b"} 會存儲 pattern "/a/b" 和 "/a/b/c"// 不會存 "/a", "b/a", "/a/c" 或 "/a/{x}" 等segments map[routingIndexKey][]*pattern// 存儲所有以多段通配符結尾的 pattern(包括結尾斜杠)。// 由于這種 pattern 數量通常不多,不做復雜索引優化。multis []*pattern
}
  • index的目的
    ServeMux 在注冊新路由時,需要判斷是否與已有 pattern 沖突(例如 /api/users 與 /api/{id})。
    routingIndex 提供快速索引,避免每次都掃描所有 pattern。
  • segments字段的解釋
    以 {位置, 字面值} 為 key,把所有對應 pattern 歸類。
    這樣在檢測沖突時,只需看 片段位置和字面值匹配的 pattern,排除不可能沖突的 pattern,提高效率。
  • multis的解釋
    用于存儲以 * 結尾的多段通配符 pattern。
    由于數量通常少,直接列表遍歷即可,不做復雜索引。

再次總結一下:
tree → 決策樹,用于高效匹配請求 URL 找到 handler,即 請求匹配
index → 索引表,用于在注冊 pattern 或檢測沖突時快速篩選潛在沖突 pattern,即 沖突檢測與注冊優化

中間件

go沒有直接提供中間件,不過提供了接口以供在此基礎上進行開發。
這個接口就是Handler接口,這是中間件的基礎。

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

中間件的本質就是包裝一個 Handler,在調用真正的 Handler 前后增加邏輯(例如日志、鑒權、限流)。
舉例:

func loggingMiddleware(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {fmt.Println("Request:", r.Method, r.URL.Path)next.ServeHTTP(w, r) // 調用下一個 handler})
}func helloHandler(w http.ResponseWriter, r *http.Request) {fmt.Fprintln(w, "Hello, World!")
}func main() {mux := http.NewServeMux()mux.HandleFunc("/", helloHandler)// 包裝 mux 作為中間件loggedMux := loggingMiddleware(mux)http.ListenAndServe(":8080", loggedMux)
}

以下是一個完整的例子

package httptestimport ("fmt""io""net/http""testing"
)func TestClient(t *testing.T) {resp, err := http.Get("http://127.0.0.1:8080/hello?name=tom")if err != nil {t.Fatal(err)fmt.Println("Error making GET request:", err)}defer resp.Body.Close()if resp.StatusCode != http.StatusOK {t.Fatalf("Expected status OK, got %s", resp.Status)} else {fmt.Println("Response status:", resp.Status)// 讀取響應體body, err := io.ReadAll(resp.Body)if err != nil {t.Fatal(err)fmt.Println("Error reading response body:", err)}fmt.Println("Response body:", string(body))}
}
func handler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, World!")
}// http.Handle 需要實現 http.Handler 接口
type MyHandler struct{}func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello from MyHandler!")
}func TestServer(t *testing.T) {mux := http.NewServeMux()mux.HandleFunc("/", handler)mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello %s", r.URL.Query().Get("name"))})// 創建全局中間件globalMiddleware := func(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {// 全局日志fmt.Printf("全局日志: %s %s\n", r.Method, r.URL.Path)// 全局 CORSw.Header().Set("Access-Control-Allow-Origin", "*")next.ServeHTTP(w, r)})}err := http.ListenAndServe("localhost:8080", globalMiddleware(mux))if err != nil {return}
}

Gin

概覽

原生的http包自1.22后有了基數樹后,性能還是可以的,如果是很小的項目,沒有什么中間件要求可以不用框架,但是很多項目還是規模大于此的,這時候框架就比可不可少了,現在(2025年9月13日)還在維護的框架中,gin是性能不錯的一個。gin是基于http包的一個框架,其實現的壓縮前綴樹性能很不錯,提供的擴展功能也多,官方文檔是這么介紹自己的:

Gin is a web framework written in Go (Golang). It features a martini-like API with much better performance, up to 40 times faster thanks to httprouter. If you need performance and good productivity, you will love Gin.

快速示例

  • 最小 runnable 代碼(例子)
	router := gin.Default()router.GET("/ping", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "pong",})})router.Run() // listen and serve on 0.0.0.0:8080

和http包一樣簡潔,gin.Default()創建一個gin的Engine,在對應的位置注冊路由,再要調用Run()就能快速啟動,就如同http.ListenAndServe()一般。

gin的Engine是其核心組件,Engine實現了http中的Handler接口,因此可以把這個gin的Engine當做一個Handler來用,因此不用Run()啟動也是可以的,這樣就可以實現優雅停機了,下面是一個優雅停機的例子。

	router := gin.Default()router.GET("/ping", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "pong",})})server := &http.Server{Addr:           fmt.Sprintf(":%s", "your_port"),Handler:        router,MaxHeaderBytes: 1 << 20,}go func() {if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {fmt.Println("Server error:", err)}}()sig := make(chan os.Signal, 1)signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) // Ctrl+C<-sigfmt.Println("\nShutting down server...")ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()if err := server.Shutdown(ctx); err != nil {log.Println("關閉服務時發生錯誤", err)return}log.Println("服務成功關閉")

啟動函數與中間件

概述

常用啟動函數:Default vs New
Default()相比New只是設置了兩個中間件,一個日志一個恢復。

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {debugPrintWARNINGDefault()engine := New()engine.Use(Logger(), Recovery())return engine
}

使用New的話就是不設置這兩個中間件,用其他的,而中間件的本質也是一個接口gin.HandlerFunc,如同http包一樣:

// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)

調用Use方法就可以添加中間件:

func main() {engine := gin.New() // 沒有默認中間件// 手動添加所需的中間件engine.Use(gin.Logger())   // 如果你需要日志engine.Use(gin.Recovery()) // 如果你需要恢復功能engine.GET("/hello", func(c *gin.Context) {c.String(200, "Hello, World!")})engine.Run(":8080")
}

中間件執行流程:

func MyMiddleware(c *gin.Context) {// 1. 處理請求前的邏輯t := time.Now()c.Next() // 2. 關鍵:執行后續中間件和Handler// 3. 處理請求后的邏輯latency := time.Since(t)fmt.Println(latency)
}

應用場景:日志記錄、權限校驗、限流、 panic recovery。

以下是一個完整的例子:

// 自定義耗時中間件
func CostMiddleware() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now() // 記錄開始時間c.Next() // 執行后續 handler 和其他中間件duration := time.Since(start) // 計算耗時log.Printf("[COST] %s %s | %d | %v",c.Request.Method,c.Request.URL.Path,c.Writer.Status(),duration,)}
}
func TestMiddle(t *testing.T) {router := gin.New()router.Use(CostMiddleware()) // 一定是要在注冊路由之前才能生效router.GET("/ping", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "pong",})})router.Run()
}

中間件

中間件簽名
func MyMiddleware() gin.HandlerFunc {return func(c *gin.Context) {// 前置邏輯c.Next() // 調用后續中間件和最終的 handler// 后置邏輯}
}
  • 類型gin.HandlerFunc = func(*gin.Context)
  • 入參*gin.Context,貫穿整個請求生命周期,負責存取參數、請求、響應、狀態。
  • 調用方式:注冊時用 router.Use(MyMiddleware())group.Use(MyMiddleware())
執行順序

Gin 的中間件和 handler 形成一條 調用鏈 (chain),依次調用。

--> M1(before) --> M2(before) --> Handler --> M2(after) --> M1(after)
  • c.Next() 之后的代碼,在子調用返回時再執行,形成“洋蔥模型”。(不寫也行,就只有前方法沒有后方法而已)

在這里插入圖片描述

// 自定義耗時中間件
func CostMiddleware() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now() // 記錄開始時間fmt.Println("CostMiddleware start...")c.Next() // 執行后續 handler 和其他中間件duration := time.Since(start) // 計算耗時log.Printf("[COST] %s %s | %d | %v",c.Request.Method,c.Request.URL.Path,c.Writer.Status(),duration,)}
}
func M1() gin.HandlerFunc {return func(c *gin.Context) {fmt.Println("M1 before")c.Next()fmt.Println("M1 after")}
}func M2() gin.HandlerFunc {return func(c *gin.Context) {fmt.Println("M2 before")c.Next()fmt.Println("M2 after")}
}func TestMiddle(t *testing.T) {router := gin.New()router.Use(CostMiddleware(), M1(), M2()) // 一定是要在注冊路由之前才能生效router.GET("/ping", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "pong",})})router.Run()
}
/*
CostMiddleware start...
M1 before
M2 before
M2 after
M1 after
20xx/xx/xx hh:mm:ss [COST] GET /ping | 200 | 0s
*/

c.Abort() 會立即停止執行后續中間件和 handler,但不會影響當前中間件中 Abort() 之后的邏輯。

Context

Gin 的 gin.Context 和 Go 標準庫中的 context.Context 是兩個不同的概念,用途也不同。

  • gin.Context:是 Gin 框架中的一個結構體類型,用于封裝 HTTP 請求和響應的信息,以及提供一些方法,用于獲取請求和響應的信息、設置響應頭、設置響應狀態碼等操作。gin.Context 只在 Gin 框架內部使用,用于處理 HTTP 請求和響應。它與 HTTP 請求和響應一一對應,每個 HTTP 請求都會創建一個新的 gin.Context 對象,并在處理過程中傳遞。
  • context.Context:是 Go 標準庫中的一個接口類型,用于在 Goroutine 之間傳遞上下文信息。context.Context 可以在 Goroutine 之間傳遞信息,例如傳遞請求 ID、數據庫連接、請求超時等信息。context.Context 的具體實現是由各種庫和框架提供的,例如 Gin 框架中也提供了一個 gin.Context 的實現,用于在 Gin 框架中使用 context.Context

總之,gin.Context 是 Gin 框架中用于處理 HTTP 請求和響應的上下文對象,而 context.Context 是 Go 標準庫中用于在 Goroutine 之間傳遞上下文信息的接口類型。

在使用 Gin 框架時,可以通過 gin.Context 來訪問 context.Context,從而在 Gin 框架中使用上下文信息。

ref: gin學習記錄,gin.Context,context.Context - 知乎

  • 生命周期:一個請求一個 Context。
  • 核心作用:封裝請求/響應、參數解析、數據傳遞(Set/Get)。

內部實現原理

路由匹配原理(前綴樹/路由樹)

簡介

大名鼎鼎的前綴樹,算法思想和工程實踐的優秀結合范例,在http還是用map的時候,gin的這個路由樹可以很大幅度的挺高性能。
gin.go這個文件中可以找到路由樹放置的位置。

type Engine struct {RouterGroup
// ...delims           render.DelimssecureJSONPrefix stringHTMLRender       render.HTMLRenderFuncMap          template.FuncMapallNoRoute       HandlersChainallNoMethod      HandlersChainnoRoute          HandlersChainnoMethod         HandlersChainpool             sync.Pooltrees            methodTrees // 在這里maxParams        uint16maxSections      uint16trustedProxies   []stringtrustedCIDRs     []*net.IPNet
}

這個數據結構是tree.go這個文件中的。

type methodTrees []methodTree
type methodTree struct {method string // 每個 HTTP Method(GET/POST/...)維護一棵獨立的路由樹root   *node
}
type node struct {path      string // 存放該節點所表示的路徑片段(可能是靜態字符串,也可能是參數段 :id 或通配符 *filepath)indices   string // 存放子節點的首字符,用于快速判斷往哪棵子樹走wildChild bool // 標記當前節點是否有通配符(參數/通配)子節點nType     nodeType // 節點類型:static / param / catchAll / rootpriority  uint32children  []*node // child nodes, at most 1 :param style node at the end of the arrayhandlers  HandlersChain // 命中的處理函數鏈,如同ServeMux.tree中的HandlerfullPath  string 
}
r.GET("/user/:id/profile", handler)

Gin 會做以下事情:

  1. 找到 GET 方法對應的路由樹根節點
  2. 把路徑 "/user/:id/profile"/ 分割成片段:["user", ":id", "profile"]
  3. 從根節點開始逐層插入:
    • user → 靜態節點
    • :id → 參數節點(wildChild=true)
    • profile → 靜態節點,掛上最終 handler

這棵路由樹長這樣(偽圖示):

(root)└── "user"└── ":id"   (wild param)└── "profile" → handler
與原生樹對比
  • 靜態路由(如 /ping)
    ServeMux 和 Gin 都很快,幾乎差不多
  • 動態路由 / 參數路由
    Gin 明顯快,因為 ServeMux 不支持參數,需要自己解析
  • 通配符匹配
    Gin 更靈活,內置解析
    ServeMux 只能末尾 *,額外處理開銷由用戶承擔

總結性能:

  • Gin 更適合復雜路由(參數、通配符、多 Method)
  • ServeMux 適合簡單靜態路由、高并發短路徑匹配

Context 的復用(sync.Pool)

Gin Context 復用原理
Gin 中,每一個 HTTP 請求都會創建一個 *gin.Context 對象,負責:

  • 保存請求和響應對象(*http.Requesthttp.ResponseWriter
  • 管理中間件鏈執行
  • 存儲路徑參數(c.Params
  • 保存狀態(c.Writer.Status()c.Errors 等)
    如果每次請求都 new 一個 Context,會導致 大量內存分配和 GC,在高并發場景下性能損失明顯。
    為了解決這個問題,Gin 使用了 sync.Pool 來復用 Context。
  • 中間件鏈實現思路
  • Recovery 的實現要點…

等等一系列功能的基石作用,復用的源碼在下面:

// 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 = reqc.reset()engine.handleHTTPRequest(c)engine.pool.Put(c)
}

每次都復用,這樣性能好一些。


以上皆為學習筆記,難免有不準確的地方

ref
Introduction | Gin Web Framework
一步一步分析Gin框架路由源碼及radix tree基數樹 - 九卷技術 - 博客園

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/97842.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/97842.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/97842.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Vision Transformer (ViT) :Transformer在computer vision領域的應用(三)

Experiment 上來的一段話就概括了整章的內容。 We evaluate the representation learning capabilities of ResNet, Vision Transformer (ViT), and the hybrid. 章節的一開頭就說明了,對比的模型就是 ResNet,CNN領域中的代碼模型。 ViT。 上一篇中提到的Hybrid模型,也就是…

5-12 WPS JS宏 Range數組規范性測試

Range()數組是JS宏中不缺少的組成部分,了解Range()數組的特性必不可少,下面我們一起測試一下各種Range()數組。 1.Range()數組特性 單元格區域:Range("a2:m2")與Range("a2","m2")的類型都是:Range/Object,功能都為單元格區域,功能…

uniapp微信小程序保存海報到手機相冊canvas

在uniapp中實現微信小程序保存海報到手機相冊&#xff0c;主要涉及Canvas繪制和圖片保存。以下是關鍵步驟和代碼示例&#xff1a; 一、關鍵代碼展示&#xff1a; 1. 模板配置&#xff1a;頁面展示該海報&#xff0c;可直接查看&#xff0c;也可下載保存到手機相冊&#xff0c;h…

glib2-2.62.5-7.ky10.x86_64.rpm怎么安裝?Kylin Linux RPM包安裝詳細步驟

一、準備工作 ?確認系統版本? 這個包是 ky10的&#xff08;也就是 openEuler 20.03 LTS SP3 或類似版本&#xff09;&#xff0c;而且是 ?x86_64 架構&#xff08;就是常見的64位電腦&#xff09;?。 你要先確認你的系統是不是這個版本&#xff0c;不然可能裝不上或者出問題…

webrtc之語音活動下——VAD人聲判定原理以及源碼詳解

文章目錄前言一、高斯混合模型介紹1.高斯模型舉例1&#xff09;定義2&#xff09;舉例說明2.高斯混合模型(GMM)1&#xff09;定義2&#xff09;舉例說明3&#xff09;一維曲線二、VAD高斯混合模型1.模型訓練介紹1&#xff09;訓練方法2&#xff09;訓練結果2.噪聲高斯模型分布1…

【Redis】-- 主從復制

文章目錄1. 主從復制1.1 主從復制是怎么個事&#x1f914;1.2 拓撲結構1.2.1 一主一從拓撲1.2.2 一主多從拓撲1.2.3 樹形拓撲1.3 主從復制原理1.3.1 復制過程1.3.2 數據同步PSYNC1.3.2.1 replicationid/replid (復制id)1.3.2.2 復制偏移量維護1.3.3 psync運行流程1.3.4 全量復制…

開源炸場!阿里通義千問Qwen3-Next發布:80B參數僅激活3B,訓練成本降90%,長文本吞吐提升10倍?

開源炸場&#xff01;阿里通義千問Qwen3-Next發布&#xff1a;80B參數僅激活3B&#xff0c;訓練成本降90%&#xff0c;長文本吞吐提升10倍? 開源世界迎來震撼突破&#xff01; 通義千問團隊最新發布的Qwen3-Next架構&#xff0c;以其獨創的"小而精"設計理念&#x…

【C++入門】C++基礎

目錄 1. 命名空間 1.1 命名空間的創建和使用 2. 輸入輸出 2.1 輸出 2.2 輸入 3. 缺省參數 3.1 全缺省 3.2 半缺省 4.函數重載 4.1 為什么C支持重載而C語言不支持&#xff1f; 4.1.2 編譯的四個過程 4.2 extern是什么 5.引用 5.1 引用的特性 5.1.1 引用的“隱式類…

如何往mp4視頻添加封面圖和獲取封面圖?

前言&#xff1a;大家好&#xff0c;之前有給大家分享過mp4錄像的方案&#xff0c;今天給大家分享的內容是&#xff1a;如何在添加自定義的封面圖到mp4里面去&#xff0c;以及在進入回放mp4視頻列表的時候&#xff0c;怎么獲取mp4視頻里面的封面圖&#xff0c;當然這個獲取到的…

你的第一個Transformer模型:從零實現并訓練一個迷你ChatBot

點擊 “AladdinEdu&#xff0c;同學們用得起的【H卡】算力平臺”&#xff0c;注冊即送-H卡級別算力&#xff0c;80G大顯存&#xff0c;按量計費&#xff0c;靈活彈性&#xff0c;頂級配置&#xff0c;學生更享專屬優惠。 引言&#xff1a;破除神秘感&#xff0c;擁抱核心思想 …

【20期】滬深指數《實時交易數據》免費獲取股票數據API:PythonJava等5種語言調用實例演示與接口API文檔說明

? 隨著量化投資在金融市場的快速發展&#xff0c;高質量數據源已成為量化研究的核心基礎設施。本文將系統介紹股票量化分析中的數據獲取解決方案&#xff0c;涵蓋實時行情、歷史數據及基本面信息等關鍵數據類型。 本文將重點演示這些接口在以下技術棧中的實現&#xff1a; P…

RabbitMQ如何保障消息的可靠性

文章目錄什么是消息可靠性&#xff1f;RabbitMQ消息可靠性的三個維度1. 生產者到Exchange的可靠性2. Exchange到Queue的可靠性3. Queue到消費者的可靠性核心機制詳解Publisher Confirm機制消息持久化Mandatory參數消費者確認機制&#xff08;ACK&#xff09;最佳實踐建議1. 合理…

二十、DevOps落地:Jenkins基礎入門(一)

二十、DevOps落地&#xff1a;Jenkins基礎入門&#xff08;一&#xff09; 文章目錄二十、DevOps落地&#xff1a;Jenkins基礎入門&#xff08;一&#xff09;1、DevOps初識1.1 什么是DevOps1.2 DevOps相關工具鏈1.3 什么是CICD&#xff1f;1.4 持續集成CI介紹1.5 持續交付和持…

簡單易實現的數據校驗方法Checksum

簡單易實現的數據校驗方法Checksum 在數據傳輸中&#xff0c;Checksum&#xff08;校驗和&#xff09; 扮演著 “數據完整性哨兵” 的角色。它的主要作用是 快速檢測數據在傳輸過程中是否發生了錯誤 。 下面我將詳細解釋它的作用、工作原理、優缺點以及典型應用。 核心作用&…

再次深入學習深度學習|花書筆記1

我已經兩年沒有碰過深度學習了&#xff0c;寫此文記錄學習過程&#xff0c;加深理解。 深度學習再次深入學習深度學習|花書筆記1信息論第四節 數值計算中的問題上溢出 和 下溢出病態條件優化法再次深入學習深度學習|花書筆記1 這本書說的太繁瑣了&#xff0c;如果是想要基于這…

DeerFlow實踐:華為LTC流程的評審智能體設計

目錄 一、機制設計核心邏輯 二、4 個評審點智能體機制詳解 &#xff08;一&#xff09;立項決策&#xff08;ATI&#xff09;智能體機制 1. 知識調用與匹配 2. 評審校驗流程 3. 異常處理 &#xff08;二&#xff09;投標決策&#xff08;ATB&#xff09;智能體機制 1. …

C++與Lua交互:從原理到實踐指南

核心原理&#xff1a;Lua虛擬棧機制 C與Lua能夠高效交互的核心在于Lua虛擬棧的設計&#xff0c;這是一個精巧的中立通信區&#xff0c;解決了兩種語言間的本質差異&#xff1a;特性對比CLua語言類型靜態編譯型動態解釋型數據管理明確內存布局虛擬機統一管理類型系統編譯時確定運…

CSS 編碼規范

CSS 編碼規范1 CSS1.1 編碼規范1.1.1 【強制】所有聲明必須以分號結尾1.1.2 【推薦】使用 2 個空格縮進1.1.3 【推薦】選擇器與 { 之間保留一個空格1.1.4 【推薦】屬性值規范1.1.5 【推薦】組合器規范1.1.6 【推薦】逗號分隔規范1.1.7 【推薦】注釋規范1.1.8 【推薦】右大括號規…

ORA-12514:TNS:監聽程序當前無法識別連接描述符中請求的服務

已經不止一次自己本機電腦安裝的Oracle使用plsqldev軟件登入提示這個了.一般前一天還好好的&#xff0c;今天就不行了.好好總結一下吧&#xff0c;也共大家一起借鑒.主要原因還是數據的歸檔日志因為內部內存已經耗盡&#xff0c;不能在進行歸檔導致數據庫啟動異常&#xff0c;沒…

Spring框架的JDBC模板技術和事務管理

SpringJDBCJDBC模板技術概述JDBC的模板類的使用Spring框架的事務管理配置文件方式半注解的方式純注解的方式JDBC模板技術概述 什么是 JDBC 模板技術&#xff1f; JDBC 模板技術是 Spring 框架為簡化持久層&#xff08;數據庫操作&#xff09;編程而提供的一種封裝機制&#xf…