context.withtimeout需全链路传递,http/grpc/db等所有支持context的客户端必须显式传入,否则下游无法感知取消;超时值须按sla分层设置,降级仅对transient error生效且兜底逻辑须零依赖、无副作用。

用 context.WithTimeout 包裹所有外部调用
不是“加个超时就行”,而是必须让整个调用链共享同一个 ctx,否则下游感知不到你已放弃。HTTP client、gRPC client、Redis client、数据库 driver —— 只要支持 context.Context 参数,就必须传进去。
- 错误写法:
http.Get("https://api.example.com")—— 完全不控制超时,可能卡死几分钟 - 正确写法:
http.DefaultClient.Do(req.WithContext(ctx)),且req本身也要用http.NewRequestWithContext(ctx, ...)构造 - DB 查询别只设
db.SetConnMaxLifetime,得在db.QueryContext(ctx, ...)这一层就断掉 - gRPC 调用必须用
client.GetUser(ctx, req),而非client.GetUser(context.Background(), req)
关键点:超时不是用来“等更久”,而是让下游立刻停止无效工作。如果下游没响应 ctx.Done(),那你的超时就是假的。
区分 transient error 和 permanent error 再决定是否降级
不是所有报错都该走兜底逻辑。把 user not found 当成故障去降级,等于掩盖业务语义;但把 dial tcp: i/o timeout 当成正常态,等于放任雪崩。
- 典型 transient error:
context.DeadlineExceeded、context.Canceled、net.OpError(含timeout或connection refused)、gRPC 的codes.Unavailable/codes.DeadlineExceeded - 典型 permanent error:
sql.ErrNoRows、gRPC 的codes.NotFound/codes.InvalidArgument、HTTP 404/400 响应 - 降级只对 transient error 生效;permanent error 应原样返回或转换为业务错误
建议在 service 层统一用 errors.Is(err, ...) 判断,别靠字符串匹配 —— err.Error() 会变,标准 error 类型才稳定。
降级分支必须零依赖、零延迟、无副作用
降级不是“换个接口再试一次”,而是立即返回确定结果。如果兜底逻辑还要查 Redis、发 HTTP 请求、甚至加锁,那它本身就可能成为新的瓶颈。
- 允许的操作:读本地内存变量、查 LRU cache(如
groupcache或fastcache)、拼接默认结构体、返回硬编码文案 - 禁止的操作:
redis.Client.Get(...)、http.Get(...)、db.Exec(...)、time.Sleep(...) - 兜底函数命名要有辨识度,比如
fallbackUser(...)而非getUserBackup(...),避免误当主逻辑调用 - 如果降级需访问缓存,确保该缓存已预热 —— 启动时异步加载,别等到第一次降级才触发重建
最容易被忽略的是:降级路径的 panic 不能被 recover 捕获。一旦兜底函数自己 panic,整个请求就真挂了 —— 所以它必须是纯函数式、无状态、无外部调用。
超时值不能拍脑袋定,得按依赖 SLA 和链路位置分层设
支付接口和用户头像接口的超时不该一样;网关层和内部 service 层的超时也不能套用同一数值。超时设短了,误降级多;设长了,资源卡住快。
- 上游入口(如 HTTP handler):设为整体 SLA 的 80%,比如对外承诺 99% 800ms
- 下游依赖:按其 SLO 反推,如第三方 API SLA 是 99% 400ms(留出网络+序列化开销)
- 并发调用多个依赖时,总超时 ≠ 单个超时之和 —— 要用
context.WithTimeout统一父 ctx,别给每个子调用单独设长超时 - 测试阶段用
go tool trace看真实 P99 耗时,上线后通过 Prometheus 监控http_request_duration_seconds_bucket和grpc_client_handled_latency_seconds_bucket
真正危险的不是超时本身,而是超时后还留着 goroutine 不清理 —— 每次超时都在内存里埋一颗雷,积少成多就 OOM。所以 defer cancel() 不是可选项,是必选项。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











