Revel 框架需手动集成 Redis:启动时用 go-redis/v9 初始化全局 client 并 Ping 健康检查,PoolSize 设 20、Password 显式置空;controller 中通过闭包传入 client,用 Get/Result 判 redis.Nil 未命中,SetEX 写缓存,TTL 动态计算,禁用 defer Close。

Revel 框架没有内置 Redis 支持,也不提供 revel-redis 这类“开箱即用”的缓存中间件——你得自己拿 github.com/go-redis/redis/v9 初始化一个全局 *redis.Client,并在 controller 中显式调用 Get/SetEX。
初始化 client 必须在 app 启动时完成,且带健康检查
Revel 的 app.Init 或 init() 阶段是唯一适合初始化 Redis client 的位置。不能在每个 controller 里 new,也不能塞进 revel.Controller 字段里随请求创建。
-
redis.NewClient(&redis.Options{Addr: "localhost:6379", Password: "", DB: 0, PoolSize: 20})——PoolSize别用默认值 10,高并发下容易卡住 -
Password字段必须显式设为""(空字符串),不是nil,否则认证失败静默连接超时 - 紧接着调
client.Ping(context.Background()).Err(),如果返回非nil就log.Fatal,别让服务带着断连状态上线 - 别在
revel.Interceptor或app.Filter里初始化:它们只处理请求流,不负责资源准备
在 controller 中安全读写,闭包捕获比塞 c.Params 更可靠
Revel 的 revel.Controller 是每次请求新建的,但 *redis.Client 是全局复用的。最轻量、最可控的方式是把 client 通过闭包传入 handler 函数,而不是塞进 c.Params 或 c.RenderArgs。
- 读缓存:
val, err := client.Get(c.Request.Context(), "user:123").Result();未命中要判errors.Is(err, redis.Nil),不是err == nil - 写缓存优先用
client.SetEX(c.Request.Context(), key, value, 5*time.Minute),比Set+Expire少一次网络往返 -
value必须是可 JSON 序列化的类型(字段首字母大写 +json:tag);取出来是 string,需手动json.Unmarshal([]byte(s), &v) - 绝对不要在 controller 方法末尾写
defer client.Close():client 是全局的,关了整个应用就断连
TTL 差异化只能靠业务代码分支,没有注解或配置驱动
Revel 不解析函数签名,Go 语言也没有运行时注解机制,所以不存在类似 Spring 的 @Cacheable(timeToLive = 300)。TTL 必须由你自己根据 key 类型或路由路径动态决定。
- 用 map 查表:
ttlMap := map[string]time.Duration{"user": 2*time.Hour, "config": 10*time.Minute} - 或 if 分支:
if strings.HasPrefix(key, "article:") { ttl = 1*time.Hour } - 避免硬编码:
time.Second * 300比300更易读、不易错
最容易被忽略的是错误判断方式和连接池大小——很多人写了 err == nil 判缓存未命中,结果永远走不到 fallback;还有人用默认 PoolSize=10,压测一上来就卡在 wait for connection 上。这两点不修,缓存不仅没提速,反而拖垮整个请求链路。


















