缓存需先判断响应可缓存性,依据cache-control、用户身份依赖及实时性要求;redis缓存应标准化key设计、合理设ttl并防击穿;中间件须异步写缓存且捕获响应体;失效策略应结合ttl与事件驱动,并防范穿透。

缓存什么?先明确响应的可缓存性边界
不是所有外部API响应都适合缓存。Gin本身不提供缓存逻辑,必须由你判断:Cache-Control头是否允许缓存、响应是否依赖用户身份(如Authorization)、数据是否强实时(如支付状态)。若上游API返回Cache-Control: no-store或含Set-Cookie,直接跳过缓存更安全。
常见误判点:
- 把带
X-RateLimit-Remaining这类动态头的响应全量缓存,导致下游拿到过期限流值 - 对
POST /webhook这类副作用接口做响应缓存,掩盖重复提交问题 - 忽略查询参数编码差异,如
?q=go%20lang和?q=go+lang被当作不同key缓存
用Redis做缓存层:Key设计与TTL策略
不要用原始URL做key——需标准化路径和参数顺序。推荐用fmt.Sprintf("api:%s:%s", method, sha256.Sum256([]byte(canonicalQuery)).String())生成确定性key。TTL不能拍脑袋定:
- 对天气/汇率等弱实时数据,设
300s(5分钟)足够 - 对用户资料类数据,优先用
max-age响应头中的值,fallback到3600s - 务必加
redis.SetNX(ctx, key, value, ttl)防止缓存击穿,避免多个goroutine同时回源
Gin中间件中注入缓存逻辑:避免阻塞主线程
别在c.Next()后同步写缓存——若下游API慢,缓存写入会拖累整个请求链路。正确做法是异步触发:
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
func cacheMiddleware(redisClient *redis.Client) gin.HandlerFunc {
return func(c *gin.Context) {
key := generateCacheKey(c)
val, err := redisClient.Get(c.Request.Context(), key).Result()
if err == nil {
c.Data(200, "application/json", []byte(val))
c.Abort()
return
}
// 请求继续,但缓存写入放goroutine里
go func() {
defer recover()
respBody, _ := io.ReadAll(c.Response.Body)
_ = redisClient.Set(c.Request.Context(), key, respBody, getTTLFromHeader(c))
}()
}
}
注意:c.Response.Body默认是io.NopCloser,需提前用gin.WrapF或自定义ResponseWriter捕获原始body流,否则读不到内容。
缓存失效怎么处理?别只靠TTL
纯TTL机制在数据变更时存在窗口期。更可靠的方式是结合事件驱动失效:
- 当业务系统更新了某类资源(如商品库存),主动
redis.Del(ctx, "api:GET:/v1/products/*")(支持通配需用SCAN) - 对关键接口(如
/user/profile),在用户修改资料后,用redis.Publish("cache:invalidate", "user:123")通知所有实例清理 - 避免用
redis.FlushDB()这种暴力方式——它会清掉限流、登录态等其他业务缓存
最易被忽略的是缓存穿透:恶意请求/user/999999999这种不存在ID,每次都会击穿到后端。应在缓存层对空结果也存一个短TTL(如60s)的"null"占位符。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!










