context取消本身不会报错,报错是因为未处理ctx.err()或在cancel后继续使用已关闭资源;正确做法是显式检查context.canceled/deadlineexceeded并按控制流处理,而非当作异常捕获。

Context取消本身不会报错,报错是因为你没处理 ctx.Err() 或在 cancel 后继续用了已关闭的资源。
为什么 context.Canceled 或 context.DeadlineExceeded 会冒出来?
这不是 bug,是 context 正常工作的信号。Kratos 的 gRPC/HTTP 客户端、数据库驱动、中间件都会检查 ctx.Err() 并提前返回错误。常见现象包括:
rpc error: code = Canceled desc = context canceledcontext deadline exceeded- HTTP client 返回
net/http: request canceled
这些错误说明:上游已放弃请求,你的函数及时响应了 —— 这恰恰是 context 起作用的表现。
真正的问题是:你把它当异常去 try/catch,而不是按控制流来判断。
正确做法是显式检查:
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
// 记日志,但不要 panic;可返回 HTTP 499 或 gRPC Canceled
log.Warn("request canceled by client")
return nil, err
}
gRPC 调用里超时没生效,还一直卡着?
Kratos 的 gRPC client 不会自动读取 http.Client.Timeout,也不认服务端配置。它只认你传进去的 ctx。
- 必须每次调用都传带超时的
ctx,例如:ctx, cancel := context.WithTimeout(ctx, 2*time.Second) -
defer cancel()必须写在函数入口处,不能只在某个分支里 —— 否则 return 早了,timer 还在跑,goroutine 泄漏 - 别复用一个全局
*grpc.ClientConn然后指望它“自带 timeout” —— gRPC 没这机制 - 如果用了
errgroup.Group,每个子 goroutine 都要派生自己的带超时ctx,不能共用父 ctx
HTTP client 超时设置被忽略?
Kratos 的 kratos.NewHTTPClient() 默认用的是 http.DefaultClient,而它 Timeout == 0,等同于永不超时。
- 必须新建
&http.Client{Timeout: 3 * time.Second},再传给kratos.NewHTTPClient(client.Option) - 别改
http.DefaultClient—— 全局污染,其他模块可能依赖它的默认行为 - transport 层的
DialContext超时(如Timeout、KeepAlive)只管建连,不管整个请求耗时;真正控制整条请求生命周期的,只有http.Client.Timeout或你传的ctx - 中间件里统一加
context.WithTimeout不可靠 —— Kratos 的 HTTP transport 会用你传的ctx,但如果你调用的是裸http.Do(req),它根本不会看这个 ctx
cancel() 忘 defer 导致 goroutine 泄漏
这是最隐蔽也最危险的问题。比如这样写:
func handle(ctx context.Context) error {
childCtx, cancel := context.WithTimeout(ctx, 1*time.Second)
if someCondition {
return errors.New("early exit")
}
// 忘了 defer cancel()
_, _ = http.DefaultClient.Do(req.WithContext(childCtx))
return nil
}
后果:内部 time.Timer 一直运行,直到 1 秒后触发 cancel,期间占用 goroutine 和内存。线上跑几天,泄漏几千个 timer 很常见。
安全写法只有两种:
- 一律在函数开头
defer cancel(),哪怕有多个 return 分支 - 用
context.WithDeadline替代WithTimeout(本质一样,但更明确)
复杂点在于:cancel() 是幂等的,但没 defer 就等于没关闸 —— 这个细节,上线前 Code Review 最容易漏。











