context.md - maoxiaoyue/hypgo GitHub Wiki

Context Package (pkg/context)

context 套件是 HypGo 框架的核心,提供跨協議(HTTP/1.1, HTTP/2, HTTP/3)一致的上下文封裝。透過它,你可以存取請求資訊、產生各種格式的回應(JSON、XML、HTML 等),以及在中間件(Middleware)之間傳遞狀態。

主要功能

  • 標準庫兼容: 提供方法將 HypGo 的 *Context 封裝進 context.Context,又或是取出。適合在框架之外呼叫需要標準 context.Context 的第三方函式庫。
  • 統一的回應介面: 提供多種簡便方法如 JSON(), XML(), String(), HTML() 以及 File() 以輸出不同格式的內容。
  • 內建協商(Content-Negotiation): Negotiate() 可根據客戶端的 Accept 標頭自動決定回應 JSON, XML, HTML 等合適格式。
  • 物件池(Object Pooling): ContextResponseWriterRequestMetricsParamsBufferURLValues 全面導入 sync.Pool 管理,大幅減少 GC 壓力。
  • 中間件支援: 提供 Next(), Abort(), AbortWithStatus() 等控制流程的方法,讓你可以輕鬆組建洋蔥式(Onion-like)中介軟體。
  • QUIC / HTTP/3 指標提取: 若為 HTTP/3 連線,可以直接透過 Context 取得 RTT (Round Trip Time) 與底層連線資訊。

基礎使用

在 Controller 中使用 hypcontext.Context

package user

import (
	"net/http"

	hypcontext "github.com/maoxiaoyue/hypgo/pkg/context"
)

func GetUser(c *hypcontext.Context) {
	// 從網址列取得變數 (例如 /user/:id)
	id := c.Param("id")

	// 從查詢參數取得資訊 (例如 ?lang=zh)
	lang := c.DefaultQuery("lang", "en")

	// 在上下文中傳遞值 (可用於 Middleware)
	c.Set("user_id", id)

	// 回傳 JSON
	c.JSON(http.StatusOK, map[string]interface{}{
		"id":   id,
		"lang": lang,
		"status": "active",
	})
}

中間件控制

使用 Next()Abort() 來控制請求流程:

func AuthMiddleware(c *hypcontext.Context) {
	token := c.GetHeader("Authorization")
	if token == "" {
		c.AbortWithStatusJSON(http.StatusUnauthorized, map[string]string{
			"error": "Missing token",
		})
		return // 必須 return,停止當前 func 執行
	}

	// 繼續執行下一個 handler
	c.Next()
}

在 Goroutine 內使用

如果你需要在另一個 Goroutine 繼續存取 Context 的內容,請記得呼叫 c.Copy() 產生副本,因為原本的 Context 在請求結束時會丟回物件池並清空。

func AsyncProcess(c *hypcontext.Context) {
	// 產生唯讀副本
	cCopy := c.Copy()

	go func() {
		// 在這個 Goroutine 只能使用 cCopy,不可使用原本的 c
		DoSomething(cCopy.Request.URL.Path)
	}()

	c.String(200, "Background task started")
}

GC 優化策略

Context 套件採用多項 GC 優化,針對高 QPS 場景(>100k req/s)減少暫停時間:

Map 重建替代逐一 Delete

Context.reset() 使用 make(map[...], 8) 重建 map,而非逐一 delete(k)。逐一 delete 會觸發 N 次 hash 操作和 GC write barrier,重建只產生一次分配,舊 map 由 GC 整體回收:

// 優化前:每請求 N 次 delete
for k := range c.Keys { delete(c.Keys, k) }

// 優化後:1 次 make,舊 map 整體 GC
c.Keys = make(map[string]interface{}, 8)

queryCacheformCache 則直接置 nil,延遲初始化。

Params Pool

路由參數 Params 有專屬 sync.Pool,避免每請求分配新 slice:

// 從 pool 取得(零分配)
params := hypcontext.AcquireParams(3)
defer hypcontext.ReleaseParams(params)

Router 的 makeContextParams() 已內建使用此 pool。

物件池清單

物件 容量限制
contextPool *Context 無限制
responseWriterPool *responseWriter 無限制
metricsPool *RequestMetrics 無限制
bufferPool *bytes.Buffer 64KB(超過不歸還)
urlValuesPool url.Values 128 entries
paramsPool *Params 32 entries
streamInfoPool *StreamInfo 無限制
quicConnPool *QuicConnection 無限制