全书目录

第三十二篇:Gin 完全指南 —— 高速交警中枢宇宙

第十三章:超时、取消、异步 —— 别把一辆已经掉头的车还在后台追三十公里

1 分钟 261 字 第 604 / 962 个阅读单元

Gin 到 2026-03-28 的官方文档已经把这块讲得更明确了:

*gin.Context 不是给你当标准 context.Context 到处传的。

真正该往下游传的是:

go
ctx := c.Request.Context()
#1. 给数据库、HTTP 客户端传标准 context
go
func (s *TicketService) FindByPlate(ctx context.Context, plate string) ([]Ticket, error) {
    // 这里把 ctx 继续传给数据库查询、RPC 或外部 HTTP 请求
    return nil, nil
}

func listTickets(c *gin.Context) {
    plate := c.Query("plate")
    tickets, err := service.FindByPlate(c.Request.Context(), plate)
    if err != nil {
        c.Error(err)
        return
    }

    c.JSON(http.StatusOK, gin.H{"data": tickets})
}
#2. 用中间件统一加超时
go
func TimeoutMiddleware(timeout time.Duration) gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)
        defer cancel()

        c.Request = c.Request.WithContext(ctx)
        c.Next()
    }
}
#3. 开 goroutine 时别直接拿原始 c

这是 Gin 官方文档点名强调的坑。

错误心智:

go
go func() {
    log.Println(c.Request.URL.Path)
}()

正确做法:

go
cCopy := c.Copy()
go func() {
    log.Println(cCopy.Request.URL.Path)
}()

因为 Gin 为了性能会复用 gin.Context
你跨 goroutine 乱用原始 c,很容易出数据竞争和诡异行为。

#4. 一个更底层的判断标准
text
需要跨包、跨 I/O、跨超时边界传播
-> 用 c.Request.Context()

需要在 Gin 中间件链里临时共享数据
-> 用 c.Set / c.Get

需要在 goroutine 里读 Gin 请求信息
-> 用 c.Copy()