本文转载自微信公众号「GoLang全栈」,面试作者小锟哥哥。通用转载本文请联系GoLang全栈公众号。缓存
今天是装饰小年,先祝大家小年快乐!

所以我得送一篇技术文章庆祝一下,面试
请看今天我们咋用”装饰器模式“搞定Redis的通用缓存。
首先得先搞懂啥是装饰器,学过 Java 或者 Python 的装饰同学应该不会陌生,比如这样:
复制public class Hello implements Shape {@Override
public void draw() { System.out.println("Hello"); }}1.2.3.4.5.6.7.里面的面试那个 @Override 就是装饰器,具体咋实现的通用呢?
请询问资深 Java 工程师去。
为啥叫装饰器呢?缓存
个人觉得可能看他在方法的上面,像头饰吧,装饰具体是面试不是这原因,我也不知道,通用不对别打我哈。缓存
其实,你可以理解他就是一个闭包方法,要调用被修饰的方法之前就需要先经过他,免费源码下载有点像拦路虎。
听着是不是很像中间件,其实是差不太多的逻辑啦。
但是为啥我们不直接用中间件来搞缓存呢?
中间件他一般是挂在某个路由组下面的,但是呢,我们要做缓存的又不可能整个路由组都需要做。
于是就想着用装饰器的思路去搞定这个缓存,我可以在我需要的某个方法之前戴一个装饰器就可以了。
我们这里使用 Gin 框架来搭建:
复制func UserListHandler() gin.HandlerFunc { return func(c *gin.Context) { list := db.GetUserListFromMySQL() res := gin.H{ "list": list, } c.JSON(200, res) }}func UserDetailHandler() gin.HandlerFunc { return func(c *gin.Context) { user := db.GetUserDetailListFromMySQL() res := gin.H{ "user": user, } c.JSON(200, res) }}func main() { r := gin.Default() r.GET("/user/list/:type", UserListHandler()) r.GET("/user/detail/:id", UserDetailHandler()) r.Run()}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.我们 db 部分我们就写一个模拟方法,去模拟从数据库里面读取数据:
复制package db
import "fmt"type User struct {Id int64
Name string
}func GetUserListFromMySQL() *[]User { fmt.Println("模拟从数据库获取数据...") list := make([]User,2) list[0] = User{ Id: 1, Name: "张三", } list[1] = User{ Id: 2, Name: "李四", } return &list
}func GetUserDetailListFromMySQL() *User { fmt.Println("模拟从数据库获取数据...") return &User{ Id: 2, Name: "李四", }}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.这样以来就能跑起来了。
我们使用的库是:
复制github.com/gomodule/redigo/redis
1.如果不知道怎么使用的,请参考我们往期 redis 的教程文章!
这里我粘贴下关键代码:
复制package k_redis
import ( "github.com/gomodule/redigo/redis" "time")var RedisDefaultPool *redis.Poolfunc newPool(addr string) *redis.Pool { return &redis.Pool{ MaxIdle: 3, IdleTimeout: 240*time.Second, Dial: func() (redis.Conn, error) { return redis.Dial("tcp", addr, redis.DialPassword("密码")) }, }}func init() { RedisDefaultPool = newPool("IP:端口")}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.接下来我们就可以使用 Redis 了:
复制//读
conn := k_redis.RedisDefaultPool.Get()defer conn.Close()res, err := redis.String(conn.Do("get", redisKey))fmt.Println(res)//写
conn.Do("setex", redisKey, 20, resData)1.2.3.4.5.6.7.8.我们的装饰器咋加呢?
需要在路由方法做手脚,云服务器提供商也就是这里:
复制r.GET("/user/list/:type", UserListHandler())1.我们只需要在 UserListHandler 这个方法外面再套一个方法,这个方法就是装饰器!
这个方法我们需要满足:传入的是 gin.HandlerFunc 方法,传出的也是 gin.HandlerFunc 这个即可!
但是为了通用性,我们需要加三个入参:
1、Redis里面的key规则参数 redisKeyPattern
2、Redis里面的key关键字参数 param
3、返回回去的数据参数 empty
开干,代码如下:
复制func Decorator(h gin.HandlerFunc, param string, redisKeyPattern string, empty interface{}) gin.HandlerFunc { return func(c *gin.Context) { //取Redis里面的key关键字参数
getId := c.Param(param) //根据Redis里面key的规则,生成RedisKey
redisKey := fmt.Sprintf(redisKeyPattern, getId) //从Redis里面读取数据
conn := k_redis.RedisDefaultPool.Get() defer conn.Close() res, err := redis.String(conn.Do("get", redisKey)) if err != nil { //缓存没有
log.Println("从数据库取...",err) //执行下一部分
h(c) dbRes,exists := c.Get("Result") if !exists { dbRes =empty
} //存缓存 转成字节流存
resData,_ := json.Marshal(dbRes) conn.Do("setex", redisKey, 20, resData) c.JSON(200, dbRes) }else{ log.Println("从缓存库取...") json.Unmarshal(res, &empty) c.JSON(200, empty) } }}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.里面有很多 error 我给忽略了,读者可自行根据需要处理!
这个装饰器比较关键的点在 c.Get("Result") 这个逻辑,我们之前的两个控制器方法就需要改造了!
复制func UserListHandler() gin.HandlerFunc { return func(c *gin.Context) { list := db.GetUserListFromMySQL() res := gin.H{ "list": list, } //c.JSON(200, res) c.Set("Result", res) }}func UserDetailHandler() gin.HandlerFunc { return func(c *gin.Context) { user := db.GetUserDetailListFromMySQL() res := gin.H{ "user": user, } //c.JSON(200, res) c.Set("Result", res) }}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.我们不能在这里面返回 json 数据了,而是通过 gin 的上下文进行值传递。
依次传递到装饰器里面。
所以在装饰器里面才可以通过 c.Get("Result")来获取到值!
服务器托管相关文章:
IT技术网服务器租用益华科技源码库亿华云企商汇香港云服务器源码下载IT资讯网益华IT技术论坛思维库码力社亿华科技编程之道亿华智慧云益华科技IT资讯网运维纵横汇智坊创站工坊益强科技云站无忧益强编程堂益强数据堂智能时代益强编程舍亿华云计算益强智囊团亿华云码上建站亿华灵动多维IT资讯益强科技益华科技科技前瞻益强前沿资讯全栈开发云智核极客编程益强IT技术网益强智未来亿华智造亿华互联极客码头
0.6062s , 17440.734375 kb
Copyright © 2025 Powered by 面试官:你可以写一个通用的Redis缓存”装饰器“么?,汇智坊 滇ICP备2023006006号-2