Gin使用redis缓存
概述
场景介绍(缓存系统)
这里写的是一个很简单的场景,有一张叫post
的数据表,用来存储文章id、标题、内容、作者id等这些信息。写好model、logic、controller层的代码能正常运行后,在本地环境运行时。下面是目录结构
.
├── controller
│ ├── post.go
│ ├── response.go
│ └── user.go
├── go.mod
├── go.sum
├── gredis
│ ├── keys.go
│ ├── post.go
│ ├── redis.go
│ └── user.go
├── logic
│ ├── post.go
│ └── user.go
├── main.go
├── middleware
│ └── cache.go
├── model
│ ├── post.go
│ └── user.go
└── mysql
├── mysql.go
├── post.go
└── user.go
gredis
目录的keys.go
文件中写常量,在gin处理请求时先从redis获取数据,如果不存在再去查询数据库,然后通过不同的key设置到redis中。创建Redis客户端
var (
rdb *redis.Client
ctx = context.Background()
)
func InitRedis() (err error) {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
_, err = rdb.Ping(ctx).Result()
if err != nil {
return err
}
return nil
}
创建redis中key常量
在gredis
目录中创建一个叫key.go
的文件,用来存放一些常量。
package gredis
const (
KeyUserIdSet = "CACHE/users/"
KeyPostIdSet = "CACHE/posts/"
CACHE_POSTS = "CACHE/all-posts"
CACHE_USERS = "CACHE/all-users"
)
判断redis中key存在
判断redis数据库中是否存在key,用exists key
来判断key
是否存在。这里使用ExistUserKey
函数来判断,如果返回的n == 0则说明key不存在。
// 查询key是否存在
func ExistKey(key string) bool {
n, err := rdb.Exists(ctx, key).Result()
if err != nil {
log.Println("find exist user Key error :", err)
}
if n == 0 {
log.Println(key, "key no exist")
return false
}
log.Println(key, "key exist")
return true
}
设置和获取缓存
// 根据文章Id获取缓存
func GetCachePostById(key string) (data *model.Post, err error) {
res, err := rdb.Get(ctx, key).Result()
if err != nil {
log.Println("GET redis post error:", err)
return nil, err
}
err = json.Unmarshal([]byte(res), &data)
return data, nil
}
// 根据文章Id设置缓存
func SetCachePostById(data *model.Post, postid string) (err error) {
strdata, _ := json.Marshal(data)
key := fmt.Sprintf("%s%s", KeyPostIdSet, postid)
err = rdb.Set(ctx, key, strdata, 10*time.Second).Err()
// 设置key 10秒钟的过期时间
if err != nil {
log.Println("SET redis ERROR:", err)
return err
}
return nil
}
设置为中间件
我们知道在gin框架中可以自定义中间件,如这样
func CacheMiddleware() gin.HandlerFunc {
return func(c *gin.Context){
// code ...
}
}
我们在目录结果middleware/
创建cache.go
文件,用于判断redis中是否存在某个key。如果key是存在的,那么直接返回redis中的数据,并执行c.Abort
;如果key不存在,那么执行c.Next()
,往下查询数据库并给redis设置上值。
/*
1. 功能是判断redis中是否存在key,如果存在则取出缓存并返回数据;c.Abort
2. 如果redis中key不存在,则c.Next()继续查询数据库并设置上值
*/
func CacheMiddleware(key string) gin.HandlerFunc {
return func(c *gin.Context) {
isExists := gredis.ExistUserKey(key)
if isExists == false { // 缓存不存在, 查询sql ,写入redis缓存
c.Next()
} else {
// 取出缓存
switch key {
case gredis.CACHE_POSTS:
data, _ := gredis.GetCacheAllPosts(key)
controller.ResponseSuccess(c, data)
case gredis.CACHE_USERS:
data, _ := gredis.GetCacheAllUsers(key)
controller.ResponseSuccess(c, data)
}
c.Abort()
}
}
}
在路由中加入缓存的中间件
r.GET("/getallpost", middleware.CacheMiddleware(gredis.CACHE_POSTS), controller.HandleGetAllPost)
r.GET("/getpostbypostid/:postid", middleware.CacheMiddleware(gredis.KeyPostIdSet), controller.HandleGetPostByPostId)
对比测试
做两次请求,第一次请求redis中不存在CACHE/all-posts
这个key,第二次请求将结果已经缓存到redis当中。
在执行更新和删除操作时
原子操作
所以存在间隔时间窗口,可能导致短暂从缓存获取到旧数据的情况。第二种方法使用Redis的订阅发布
功能,配合Go channel
使用,根据接收的消息删除相关缓存。这种方式是原子操作
,确保缓存与数据库同步。使用删除和设置过期时间
// 删除某个key
func DeleteKey(key string) (err error) {
if err = rdb.Del(ctx, key).Err(); err != nil {
log.Println("Delete Key ERROR:", err)
return err
}
return nil
}
// 设置key过期
func SetKeyExpired(key string) (err error) {
err = rdb.ExpireAt(ctx, key, time.Now().Add(-10*time.Second)).Err()
if err != nil {
log.Println("Set Key Expired ERROR:", err)
return err
}
return nil
}
使用发布订阅实现异步缓存失效
UpdatePost
函数发布消息
// 更新文章缓存
func UpdatePost(key string) {
rdb.Publish(ctx, "post_cache", key)
}
订阅对应的channel
// 订阅 channel
func SubChannel() {
sub := rdb.Subscribe(ctx, "post_cache")
ch := sub.Channel()
go func() {
for msg := range ch {
if err := DeleteKey(msg.Payload); err != nil {
log.Println("delete key ERROR:", err)
}
}
}()
}
在controller中调用函数
func Handlexxx(c *gin.Context){
...
gredis.UpdatePost(currentKey)
}
写在最后
文中完整代码: https://github.com/FengZeHe/LearnRedis/tree/main/redis-cache-demo
原文始发于微信公众号(ProgrammerHe):在Gin框架中简单使用redis做缓存系统
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/207918.html