超时后应直接fallback到本地缓存,需用context.withtimeout控制外部调用,区分超时与网络错误,合理设置超时时间、缓存key设计、避免雪崩(用singleflight)、安全处理错误、优化http客户端配置、规范缓存数据结构及敏感信息管理。

依赖超时后直接 fallback 到本地缓存
Go 里没有内置的“自动降级”机制,得靠自己控制执行流。核心是用 context.WithTimeout 包住外部调用,一旦超时就跳过,转而读取本地缓存(比如 sync.Map 或 bigcache)。别指望 http.Client 自己降级——它只管发请求,不负责兜底。
常见错误是把超时和错误混为一谈:ctx.Err() == context.DeadlineExceeded 才代表真超时;而 io.EOF、connection refused 这类网络错误得单独判断,它们可能意味着服务已挂,更适合触发缓存 fallback。
- 超时时间建议设得比依赖 P99 延迟高 20%~50%,太短容易误降级,太长拖慢主链路
- 缓存 key 要包含业务维度(如用户 ID、地区),不能只用原始请求参数,否则不同用户共享同一份缓存会出问题
- 如果用
sync.Map,注意它不支持 TTL,得自己加时间戳字段 + 定期清理或惰性淘汰
缓存失效时避免雪崩:加锁 + 单点重建
多个并发请求同时发现缓存过期,全去调依赖服务,很容易打垮下游——这就是缓存雪崩。Go 里最轻量的解法是用 singleflight.Group,它能保证相同 key 的请求只让一个 goroutine 去加载,其余等待结果返回。
但要注意:singleflight 不处理错误场景。如果加载失败(比如依赖返回 500),默认会把错误结果也广播给所有等待者。得手动包装一层:只在 err == nil 时才写入缓存,失败则让等待者各自重试或走兜底逻辑。
- 不要在
singleflight.Do里直接写缓存,先校验结果有效性,再由外层决定是否Set - 缓存重建失败时,可以考虑返回旧缓存(stale-while-revalidate),但需确保旧值没严重过期(比如超过 2 倍 TTL)
-
singleflight的 key 是string,别传 struct 或指针,容易因地址不同导致重复加载
HTTP 依赖不可用时快速失败并切到缓存
HTTP 客户端默认行为是重试 + 长连接复用,这在降级场景下反而有害。必须显式禁用重试、缩短连接超时,并用 http.Transport 控制底层连接行为。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
典型错误是只改了 http.Client.Timeout,却忽略 Transport.DialContext 和 Transport.ResponseHeaderTimeout。结果可能是:DNS 解析卡住 30 秒,或者 header 没回来就一直等。
- 设置
Transport.MaxIdleConnsPerHost = 10,太高会积压空闲连接,影响故障感知速度 - 用
errors.Is(err, context.DeadlineExceeded)判断超时,别用strings.Contains(err.Error(), "timeout"),不安全 - 对明确已知不可用的 endpoint(如运维标记的维护中),可加内存白名单,直接跳过 HTTP 调用,秒级 fallback
缓存数据结构设计影响降级效果
缓存内容不是越全越好。如果把整个 API 响应 JSON 原样塞进缓存,某天依赖返回字段变多,你缓存里还是老结构,前端可能 panic。更稳妥的是只缓存关键字段,或用 struct 显式定义缓存 schema。
另一个坑是时间精度:用 time.Now().Unix() 做过期判断,不如直接存 time.Time 字段。Go 的 json.Marshal 默认序列化成 RFC3339 字符串,反序列化时能正确还原时区,而秒级时间戳会丢失时区信息,跨服务部署时容易误判过期。
- 缓存 struct 里加
UpdatedAt time.Time `json:"updated_at"`字段,比靠 TTL 更可控 - 避免缓存含敏感信息(如 token、手机号),降级时可能被日志打印或监控抓取
- 如果依赖返回 protobuf,别直接缓存二进制,先转成 Go struct 再序列化,便于后续字段演进
真正难的不是写 fallback 逻辑,而是判断什么时候该降级、什么时候该报错。比如某个非核心字段缺失不影响主流程,但用户余额查询失败就必须阻断——这个边界得靠业务语义定,没法靠通用库解决。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










