Fiber中需手动实现Redis缓存:选go-redis、配置连接池、显式处理读写与空值、加前缀隔离、统一JSON序列化、同步删除失效缓存。

Cache中间件在Fiber中不是开箱即用的
Fiber本身不提供内置缓存抽象层(不像Spring Boot有@Cacheable),也没有CacheManager或自动序列化配置。所有缓存逻辑必须显式控制:选客户端、管连接、定键名、设过期、处理空值。直接套用“加个注解就缓存”的思路会卡住。
你得自己决定:
- 用
redis-go还是go-redis(推荐后者,维护活跃、支持集群/哨兵) - 缓存键是拼接字符串还是用
fmt.Sprintf生成 - 是否对
nil结果也缓存(防穿透) - 过期时间写死还是按业务动态算(比如用户数据5分钟,商品详情30分钟)
用 go-redis 配置 Redis 客户端并复用连接池
Fiber是HTTP服务器,不是框架容器,不管理依赖生命周期。你必须手动初始化redis.Client,并在应用启动时建立连接,不能每次请求都redis.NewClient()。
import "github.com/redis/go-redis/v9"
<p>var rdb *redis.Client</p><p>func initRedis() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password
DB: 0,
PoolSize: 20, // 根据QPS调,1000 QPS建议≥30
MinIdleConns: 5,
DialTimeout: 5 <em> time.Second,
ReadTimeout: 5 </em> time.Second,
WriteTimeout: 5 <em> time.Second,
})
ctx, cancel := context.WithTimeout(context.Background(), 5</em>time.Second)
defer cancel()
if err := rdb.Ping(ctx).Err(); err != nil {
log.Fatal("failed to connect to Redis:", err)
}
}
注意:
-
PoolSize设太小会导致redis: connection pool exhausted错误 - 不要漏掉
ctx超时控制,否则网络抖动时整个HTTP handler会被拖住 -
initRedis()应在main()开头调用,早于app.Listen()
手动实现缓存读写逻辑,避免常见空指针和类型错误
Fiber路由里没法像Spring那样自动代理方法。你得在Handler里显式查缓存 → 命中则返回 → 未命中则查DB → 写缓存 → 返回。
func getUserHandler(c *fiber.Ctx) error {
userID := c.Params("id")
cacheKey := "user:" + userID
<pre class="brush:php;toolbar:false;">var user User
err := rdb.Get(context.Background(), cacheKey).Scan(&user)
if err == nil {
return c.JSON(user)
}
if err != redis.Nil { // 真实错误,比如连接失败
return c.Status(500).SendString("cache error")
}
// 缓存未命中,查数据库
dbUser, err := db.FindUserByID(userID)
if err != nil {
return c.Status(404).SendString("not found")
}
// 写入缓存,TTL 10 分钟
err = rdb.Set(context.Background(), cacheKey, dbUser, 10*time.Minute).Err()
if err != nil {
log.Printf("failed to set cache: %v", err)
// 不影响主流程,继续返回
}
return c.JSON(dbUser)}
关键点:
-
err == redis.Nil才是缓存未命中;其他err是连接或协议错误,不能忽略 -
rdb.Get().Scan()要求目标结构体字段有导出(首字母大写)且tag匹配,否则静默失败 -
rdb.Set()失败不应阻断响应,尤其生产环境要避免缓存写失败导致接口500
高频场景下必须加前缀隔离和统一序列化
多个服务共用一个Redis实例时,没前缀会导致键冲突;不同结构体混存会导致Scan()反序列化失败。
- 所有键强制加项目前缀:
myapp:user:123、myapp:product:456 - 统一用
json.Marshal/Unmarshal,别依赖encoding/gob(跨语言不兼容) - 对空结果也缓存(如
rdb.Set(ctx, key, "null", 1*time.Minute)),防止缓存穿透
最易被忽略的是:Fiber中间件无法自动感知缓存失效。你删数据库记录时,必须同步rdb.Del()对应key——没有@CacheEvict帮你兜底。


















