您现在的位置是:亿华云 > 应用开发

面试官:你可以写一个通用的Redis缓存”装饰器“么?

亿华云2025-10-02 18:49:14【应用开发】6人已围观

简介本文转载自微信公众号「GoLang全栈」,作者小锟哥哥。转载本文请联系GoLang全栈公众号。 今天是小年,先祝大家小年快乐!所以我得送一篇技术文章庆祝一下,请看今天我们咋用”装饰器模式“搞定Redi

本文转载自微信公众号「GoLang全栈」,面试作者小锟哥哥。通用转载本文请联系GoLang全栈公众号。缓存  

今天是装饰小年,先祝大家小年快乐!

面试官:你可以写一个通用的Redis缓存”装饰器“么?

所以我得送一篇技术文章庆祝一下,面试

面试官:你可以写一个通用的Redis缓存”装饰器“么?

请看今天我们咋用”装饰器模式“搞定Redis的通用缓存。

面试官:你可以写一个通用的Redis缓存”装饰器“么?

啥是缓存装饰器模式?

首先得先搞懂啥是装饰器,学过 Java 或者 Python 的装饰同学应该不会陌生,比如这样:

public class Hello implements Shape {

@Override

public void draw() {

System.out.println("Hello");

}

}

里面的面试那个 @Override 就是装饰器,具体咋实现的通用呢?

请询问资深 Java 工程师去。

为啥叫装饰器呢?缓存

个人觉得可能看他在方法的上面,像头饰吧,装饰具体是面试不是这原因,我也不知道,通用不对别打我哈。缓存

其实,你可以理解他就是一个闭包方法,要调用被修饰的方法之前就需要先经过他,云南idc服务商有点像拦路虎。

听着是不是很像中间件,其实是差不太多的逻辑啦。

但是为啥我们不直接用中间件来搞缓存呢?

中间件他一般是挂在某个路由组下面的,但是呢,我们要做缓存的又不可能整个路由组都需要做。

于是就想着用装饰器的思路去搞定这个缓存,我可以在我需要的某个方法之前戴一个装饰器就可以了。

先实现一个传统的API

我们这里使用 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()

}

我们 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: "李四",

}

}

这样以来就能跑起来了。

预热下 Redis

我们使用的库是:

github.com/gomodule/redigo/redis

如果不知道怎么使用的,请参考我们往期 redis 的教程文章!

这里我粘贴下关键代码:

package k_redis

import (

"github.com/gomodule/redigo/redis"

"time"

)

var RedisDefaultPool *redis.Pool

func 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:端口")

}

接下来我们就可以使用 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)

编写装饰器

我们的装饰器咋加呢?

需要在路由方法做手脚,香港云服务器也就是这里:

r.GET("/user/list/:type", UserListHandler())

我们只需要在 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)

}

}

}

里面有很多 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)

}

}

我们不能在这里面返回 json 数据了,而是通过 gin 的上下文进行值传递。

依次传递到装饰器里面。

所以在装饰器里面才可以通过 c.Get("Result")来获取到值!

亿华云计算

很赞哦!(9461)