登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go context 超时控制:把 deadline 传到每个下游调用

来源:Golang学习网专题原创

时间:2026-06-08 18:06:00 586浏览 收藏

没有超时的并发就像没有刹车的车。请求入口已经超时,内部 goroutine 还在查数据库、等 HTTP 响应,这会持续消耗连接池和内存。context 的价值,就是让 deadline 成为调用链的一部分。

入口设置总预算

总预算应该从用户体验和服务 SLO 反推。例如接口希望 1 秒内返回,可以给业务处理 800ms,留下序列化和网络抖动的余量。不要在每个下游随意设置互相矛盾的 timeout。

下游分配子预算

并不是所有下游都能拿到完整预算。缓存可以 50ms,数据库可以 200ms,外部 HTTP 可以 300ms。子预算应该小于父 context 的剩余时间。

取消不是错误噪音

context.Canceled 和 DeadlineExceeded 应该被统计,但不一定都算业务错误。用户主动断开、上游超时和本服务处理超时要区别分析。

生产场景

适用于所有线上 HTTP API、RPC 调用、SQL 查询和后台任务。只要请求可能跨进程或访问外部资源,就应该让 deadline 成为参数的一部分。

关键指标

  • DeadlineExceeded、Canceled 与业务错误的比例
  • 父 context 剩余时间与下游子预算的匹配度
  • 超时发生时连接池、队列和 goroutine 的变化

常见误区

  • 每个下游各自设置 timeout,导致总耗时超过入口预算
  • 捕获 context 错误后继续重试
  • 把用户取消当成服务端错误报警

落地建议

建议由入口统一创建总预算,再按下游重要性拆分子预算。日志里保留 request id、deadline、依赖名和剩余耗时,便于定位是哪一层消耗了预算。

代码示例

func Query(ctx context.Context, id int64) (*DTO, error) {
    dbCtx, cancel := context.WithTimeout(ctx, 200*time.Millisecond)
    defer cancel()

    row := db.QueryRowContext(dbCtx, "select name from user where id=?", id)
    if err := row.Err(); err != nil {
        return nil, err
    }
    return dto, nil
}

上线检查

  • 入口 context 不被替换为 context.Background。
  • 数据库、HTTP、缓存都使用支持 context 的 API。
  • 日志里区分 canceled 和 deadline exceeded。
声明:本文转载于:Golang学习网专题原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>