Beego中间件加缓存需手动控制执行流:命中缓存后调WriteHeader/Write/Flush,再panic(beego.ErrAbort)中断流程;key应含路径、query和Accept头;生产环境必须用Redis存储,避免内存缓存问题。

Beego 中间件里怎么加缓存逻辑?别直接改 context.Output
Beego 的中间件(beego.InsertFilter)本身不提供「自动响应拦截」能力,不像 Gin 的 c.Abort() + c.Data 那样能轻易终止后续链路并返回缓存。很多人误以为在中间件里调用 ctx.Output.Body() 就算完成缓存响应,结果发现:路由函数仍会继续执行、日志重复打、DB 查询照常发起——缓存完全失效。
正确做法是手动控制执行流:在命中缓存后,必须显式调用 ctx.ResponseWriter.WriteHeader() 和 ctx.ResponseWriter.Write(),再调用 ctx.ResponseWriter.Flush(),最后用 panic(beego.ErrAbort) 中断 Beego 的默认处理流程。Beego 内部靠捕获这个特定 panic 来跳过后续 handler 和模板渲染。
- 缓存 key 建议包含请求路径 + query string + Accept 头(区分 JSON/HTML)
- 避免缓存 POST/PUT 等非幂等方法,只对 GET/HEAD 生效
- 中间件注册顺序很重要:
beego.InsertFilter("/api/*", beego.BeeApp.Handlers, cacheMiddleware, true)的第三个参数设为true表示前置执行
缓存数据存在哪?Redis 比内存 map 更可靠
用 sync.Map 或全局 map[string][]byte 存缓存看似简单,但很快会遇到问题:内存无上限、无法跨进程共享(多实例部署时缓存不同步)、重启即丢失、没 TTL 自动清理。生产环境必须用 Redis。
Beego 自带的 cache 模块(bee/cache)支持 Redis 后端,但注意它默认不校验响应状态码——你得自己在中间件里判断 ctx.Input.StatusCode == 200 才写入;否则 500 错误也可能被缓存并反复返回。
- Redis key 命名建议加前缀,如
beego:cache:api:/users?role=admin - 设置合理 TTL,比如 5–30 分钟;高频变动接口(如用户余额)慎用缓存
- 用
redis.Conn.Do("SET", key, data, "EX", ttlSeconds)而非SetEx,避免 Go redis 客户端版本差异导致命令不兼容
如何避免缓存穿透和雪崩?加一层空值标记
当大量请求查一个根本不存在的 ID(比如 /user/999999999),缓存不命中,所有请求直击 DB,这就是缓存穿透。更危险的是缓存雪崩:如果所有热点 key 在同一时间过期,瞬间流量全压到后端。
解决方案不是“不缓存空结果”,而是缓存一个明确的空标记(如 "null" 字节数组),并设较短 TTL(如 2 分钟)。这样既挡掉无效请求,又不会长期占用内存。
- 检查缓存时,先
redis.Conn.Do("GET", key),若返回"null",直接返回 404 并panic(beego.ErrAbort) - DB 查询结果为空时,不要跳过写缓存步骤,而是写入空标记
- 雪崩防护:给 TTL 加随机偏移,比如基础 300 秒,再 ±60 秒
缓存内容怎么压缩?gzip 不在中间件里做
有人想在中间件里对缓存 body 做 gzip 压缩再存 Redis,省带宽。这很危险:Beego 的 Output 模块会在 response 写出前自动加 Content-Encoding: gzip 头,但如果你提前压缩了 body 却没设头,客户端就解不开;反之,如果设了头但没压缩,客户端又会报错。
真正安全的做法是:缓存原始未压缩 body,把压缩交给 Beego 自己处理。确保 beego.BConfig.WebConfig.EnableGzip = true,并在中间件写响应前,让 Beego 的 GzipWriter 正常包裹 ResponseWriter。只要你不替换 ctx.ResponseWriter,Beego 的 Gzip 中间件就能生效。
- 不要在中间件里调
gzip.NewWriter()手动压缩 - 确认
beego.BConfig.WebConfig.GzipLevel设为1(最快)或6(平衡),避免高 CPU 占用 - 浏览器是否接受 gzip,由
Accept-Encoding请求头决定,Beego 默认已做判断
缓存逻辑嵌在中间件里看似轻量,但涉及执行流控制、存储选型、异常兜底、压缩协同等多个耦合点,漏掉任意一环都可能让缓存变成性能负优化。最易忽略的是 panic 中断机制——没它,缓存就只是个旁路日志。











