Fiber 框架不内置缓存中间件或声明式缓存抽象,需手动结合 Redis 客户端与业务逻辑实现差异化 TTL;推荐直接使用 redis.Client.SetEX 显式设置过期时间,并通过策略映射或封装 Cache 结构体管理不同业务类型的 TTL。

Fiber 框架本身不内置 Cache 中间件,也没有类似 Spring @Cacheable 那样的声明式缓存抽象 —— 所以不存在“配置 Cache 中间件设置差异化过期时间”这种开箱即用的方案。你得自己组合 Redis 客户端 + 业务逻辑来实现。
为什么 Fiber 没有 cacheManager 或 timeToLive 属性
Fiber 是 Go 语言的轻量 HTTP 框架,专注路由、中间件和请求生命周期管理。它不提供缓存策略层,更不会像 Spring 那样抽象出 CacheManager、CacheConfiguration 或伪造的 timeToLive 注解字段。所有缓存控制必须落在具体客户端(如 redis.Client)和业务代码里。
- 你在 Fiber 中写
@Cacheable(cacheNames="user", timeToLive=300)?Go 里压根没这语法,也不会编译通过 - Fiber 的中间件只负责拦截
fiber.Ctx,不解析函数签名、不代理返回值、不自动序列化/反序列化 - 所谓“缓存中间件”,在 Fiber 里通常只是个封装了
redis.Client的结构体,外加几个Get/Set方法
按 key 前缀或业务类型设不同 TTL:用 redis.Client.SetEX
最直接、最可控的方式是绕过任何“中间件封装”,在 handler 里调用 redis.Client.SetEX(或 Set + Expire),显式传入 time.Duration。TTL 差异化就靠 if/else 或 map 查表。
// 示例:根据 resourceType 决定 TTL
func handleUserDetail(c *fiber.Ctx) error {
userID := c.Params("id")
key := "user:" + userID
<pre class="brush:php;toolbar:false;">// 查缓存
val, err := rdb.Get(c.Context(), key).Result()
if err == redis.Nil {
// 缓存未命中,查 DB
user, _ := db.FindUserByID(userID)
// 写入缓存:用户数据过期 2 小时
rdb.SetEX(c.Context(), key, user, 2*time.Hour)
return c.JSON(user)
} else if err != nil {
return err
}
return c.SendString(val)}
func handleToken(c *fiber.Ctx) error { token := c.Query("t") key := "token:" + token
// 令牌类数据过期 5 分钟 rdb.SetEX(c.Context(), key, "valid", 5*time.Minute) return c.SendStatus(fiber.StatusOK)
}
-
SetEX是原子操作,比Set+Expire更安全(避免 key 写入成功但 expire 失败) - 别用
SET命令手动拼SETEX key 300 value——redis.Client已封装好语义明确的方法 - TTL 值不要硬编码,建议从 config 或 context 中提取,比如
cfg.TTL.User/cfg.TTL.Token
想复用逻辑?封装一个带策略的 Cache 结构体
如果你有多个 handler 都要按 type 设 TTL,可以抽一个 Cache 类型,内部用 map[string]time.Duration 管理策略,而不是幻想有个全局 cacheManager。
type Cache struct {
client *redis.Client
ttlMap map[string]time.Duration // key: cacheType, value: TTL
}
<p>func (c <em>Cache) Set(ctx context.Context, cacheType, key string, value interface{}) error {
ttl, ok := c.ttlMap[cacheType]
if !ok {
ttl = 10 </em> time.Minute // 默认 fallback
}
return c.client.SetEX(ctx, key, value, ttl).Err()
}</p><p>// 使用
var userCache = &Cache{
client: rdb,
ttlMap: map[string]time.Duration{
"user": 2 <em> time.Hour,
"config": 24 </em> time.Hour,
"session": 30 * time.Minute,
},
}- 这个
Cache不是 Fiber 官方中间件,也不注册进app.Use(),它只是个工具对象 - 每个 cacheType 对应一个 TTL,不是每个 key —— 如果真要 per-key 动态算 TTL(比如按用户等级),就在
Set方法里加参数或回调函数 - 注意:Go 的
redis.Client默认复用连接池,不用为每个 TTL 策略建新 client,也不存在 Spring 里多RedisCacheManager导致连接数爆炸的问题
别踩的坑:序列化、空值、并发覆盖
Go 里没有 Spring 那套序列化自动兜底机制,所有细节都得你管:
-
rdb.SetEX(..., userStruct, ...)要确保userStruct可被json.Marshal(或你自定义的序列化器)处理,否则存进去是空字符串或 panic - 缓存空结果?
redis.Nil是查询返回的错误,不是值;你要显式存"null"或用布隆过滤器防穿透,Fiber 不帮你做 - 并发写同一个 key?
SetEX是原子的,但如果你先Get再SetEX(非原子读-改-写),就会有竞态 —— 这种场景该用 Lua 脚本或redis.Client.SetNX控制 - 用
github.com/go-redis/redis/v9?确认你调的是SetEX,不是已废弃的Set(v8 里叫Set,v9 改名了)
真正麻烦的从来不是“怎么设 TTL”,而是“谁来保证缓存与 DB 一致”“空值怎么存”“并发更新怎么防”。Fiber 把选择权全交给你,也意味着所有责任都在你手上 —— 这就是它轻量的前提。


















