全书目录

第三十三篇:Fiber 完全指南 —— 极简高压快线城区宇宙

第八章:`fiber.Ctx` 和 `context.Context` —— 同名亲戚,不是完全一回事

1 分钟 428 字 第 619 / 962 个阅读单元

Fiber 官方 Go Context 文档有个很容易让人误判的点:

fiber.Ctx 实现了 context.Context 接口。

所以你能这样写:

go
func doWork(ctx context.Context) error {
    return nil
}

app.Get("/sync", func(c fiber.Ctx) error {
    return doWork(c)
})

这在“当前 handler 内”是可行的。

但官方也提醒了一个关键细节:

  • fiber.Ctx 虽然实现了 context.Context
  • 但它的 DeadlineDoneErr 目前不是你期待中的完整取消语义

所以你不能天真地把它等同于“标准请求上下文的一切行为都可靠”。


#1. handler 外请用 c.Context()

如果你要把上下文带到 handler 外,比如异步任务、goroutine,请这样:

go
app.Get("/job", func(c fiber.Ctx) error {
    ctx := c.Context()

    go func(ctx context.Context) {
        _ = doWork(ctx)
    }(ctx)

    return c.SendStatus(fiber.StatusAccepted)
})

官方文档明确说了:

c.Context() 返回的 context.Context 才是可以在 handler 完成后继续安全使用的。

如果需要自定义:

go
app.Get("/job", func(c fiber.Ctx) error {
    c.SetContext(context.WithValue(context.Background(), "requestID", "abc-123"))
    ctx := c.Context()

    go func(ctx context.Context) {
        _ = doWork(ctx)
    }(ctx)

    return c.SendStatus(fiber.StatusAccepted)
})

#2. 想做超时控制,自己包一层
go
app.Get("/slow", func(c fiber.Ctx) error {
    ctx, cancel := context.WithTimeout(c.Context(), 2*time.Second)
    defer cancel()

    resultCh := make(chan string, 1)

    go func() {
        select {
        case <-time.After(3 * time.Second):
            select {
            case <-ctx.Done():
                return
            case resultCh <- "done":
            }
        case <-ctx.Done():
            return
        }
    }()

    select {
    case res := <-resultCh:
        return c.SendString(res)
    case <-ctx.Done():
        return c.Status(fiber.StatusGatewayTimeout).SendString("timeout")
    }
})

这才是快线城区里比较靠谱的做法:

别假设请求结束后所有后台活会自动消失。你要自己设计取消和超时。