redis.go
package initilize
import (
"fmt"
"log"
"time"
"xxxx/common/global"
"xxxx/config"
"github.com/garyburd/redigo/redis"
)
/**
* 初始化redis,并赋值给全局变量
*/
func InitilizeRedis() {
// 创建redis连接池
global.REDISPoll = GetRedisPool(global.CONFIG)
}
func GetRedisPool(config config.Config) *redis.Pool {
return &redis.Pool{
MaxIdle: config.Redis.MaxIdle, // 最大空闲连接数
MaxActive: config.Redis.Active, // 最大连接数
IdleTimeout: time.Duration(config.Redis.Timeout) * time.Second,
Wait: true, // 超过连接数后是否等待
Dial: func() (redis.Conn, error) {
redisUri := fmt.Sprintf("%s:%s", config.Redis.Addr, config.Redis.Port)
var redisConn redis.Conn
var err error
if config.Redis.Auth {
redisConn, err = redis.Dial("tcp", redisUri, redis.DialPassword(config.Redis.Password))
} else {
redisConn, err = redis.Dial("tcp", redisUri)
}
if err != nil {
log.Println("获取连接失败:" + err.Error())
return nil, err
}
// 添加 Redis 前缀
// if config.Redis.Prefix != "" {
// redisConn = &PrefixedConn{Conn: redisConn, Prefix: config.Redis.Prefix}
// }
return redisConn, nil
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
if time.Since(t) < time.Minute {
return nil
}
_, err := c.Do("PING")
return err
},
}
}
// PrefixedConn 是一个实现了 redis.Conn 接口的自定义结构体,它在键名前添加了前缀
type PrefixedConn struct {
redis.Conn
Prefix string
}
// Do 实现了 redis.Conn 接口的 Do 方法
func (c *PrefixedConn) Do(commandName string, args ...interface{}) (interface{}, error) {
// todo
return c.Conn.Do(commandName, args...)
}
util.go
package util
import (
"encoding/json"
"fmt"
"xxxx/task-flow-api/common/global"
"github.com/garyburd/redigo/redis"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/spf13/cast"
)
// 简单分布式锁 -- 暂时使用在上传和下载
// 看门狗自动续时 暂不实现
// 分布式ID避免高并发时误释放 暂不实现
// lua保证原子性 暂不实现
// 保证这些特性可以去使用三方的库(github.com/go-redsync/redsync)
const DefaultRedisLockExpire = 300
func GetRedisLockKey(key string) string {
return "lock:" + key
}
type RedisLock struct {
key string
expire int
}
func NewRedisLock(key string, expire int) *RedisLock {
return &RedisLock{
key: GetRedisLockKey(key),
expire: expire,
}
}
func NewUserRedisLock(ctx *gin.Context, key string, expire int) *RedisLock {
return &RedisLock{
key: GetRedisLockKey(fmt.Sprintf("%d:%s", ctx.GetInt("user_id"), key)),
expire: expire,
}
}
func NewLesseeRedisLock(ctx *gin.Context, key string, expire int) *RedisLock {
return &RedisLock{
key: GetRedisLockKey(fmt.Sprintf("%d:%s", ctx.GetInt("lessee_id"), key)),
expire: expire,
}
}
func (l *RedisLock) TryLock() bool {
conn := global.REDISPoll.Get()
defer conn.Close()
txid := uuid.New().String()
res, err := conn.Do("SET", l.key, txid, "NX", "EX", l.expire)
if err != nil {
return false
}
return cast.ToString(res) == "OK"
}
func (l *RedisLock) UnLock() error {
conn := global.REDISPoll.Get()
defer conn.Close()
_, err := conn.Do("DEL", l.key)
return err
}
const DefaultRedisItemEx = 300
type RedisItemInterface interface {
ExpiresAfter(exp int)
}
type RedisItem struct {
Exp int
}
func (r *RedisItem) ExpiresAfter(exp int) {
r.Exp = exp
}
func GetSetAny[T any](key string, fun func(item RedisItemInterface) (T, error)) (result T, err error) {
// 获取缓存
conn := global.REDISPoll.Get()
defer conn.Close()
r, err := conn.Do("GET", key)
if err != nil {
return result, err
}
if r != nil {
// 返回
err = json.Unmarshal([]byte(cast.ToString(r)), &result)
return
}
// 创建 RedisItem 实例
redisItem := &RedisItem{}
result, err = fun(redisItem)
if err != nil {
return result, err
}
// 存储缓存
resStr, err := json.Marshal(result)
if err != nil {
return result, err
}
if redisItem.Exp == -1 {
_, err = conn.Do("SET", key, resStr)
} else {
_, err = conn.Do("SET", key, resStr, "EX", redisItem.Exp)
}
return
}
func GetSetString(key string, fun func(item RedisItemInterface) (string, error)) (result string, err error) {
// 获取缓存
conn := global.REDISPoll.Get()
defer conn.Close()
r, err := conn.Do("GET", key)
if err != nil {
return "", err
}
if r != nil {
// 返回
result = cast.ToString(r)
return
}
// 创建 RedisItem 实例
redisItem := &RedisItem{}
result, err = fun(redisItem)
if err != nil {
return "", err
}
// 存储缓存
if redisItem.Exp == -1 {
_, err = conn.Do("SET", key, result)
} else {
_, err = conn.Do("SET", key, result, "EX", redisItem.Exp)
}
return
}
func GetSetInt(key string, fun func(item RedisItemInterface) (int, error)) (result int, err error) {
// 获取缓存
conn := global.REDISPoll.Get()
defer conn.Close()
r, err := conn.Do("GET", key)
if err != nil {
return 0, err
}
if r != nil {
// 返回
result, err = redis.Int(r, err)
return
}
// 创建 RedisItem 实例
redisItem := &RedisItem{}
result, err = fun(redisItem)
if err != nil {
return
}
// 存储缓存
if redisItem.Exp == -1 {
_, err = conn.Do("SET", key, result)
} else {
_, err = conn.Do("SET", key, result, "EX", redisItem.Exp)
}
return
}
func GetSetInt64(key string, fun func(item RedisItemInterface) (int64, error)) (result int64, err error) {
// 获取缓存
conn := global.REDISPoll.Get()
defer conn.Close()
r, err := conn.Do("GET", key)
if err != nil {
return 0, err
}
if r != nil {
// 返回
result, err = redis.Int64(r, err)
return
}
// 创建 RedisItem 实例
redisItem := &RedisItem{}
result, err = fun(redisItem)
if err != nil {
return
}
// 存储缓存
if redisItem.Exp == -1 {
_, err = conn.Do("SET", key, result)
} else {
_, err = conn.Do("SET", key, result, "EX", redisItem.Exp)
}
return
}
func TestRedisLock(t *testing.T) {
// 从连接池取出一个连接
conn := global.REDISPoll.Get()
defer conn.Close() // 关闭连接池,一旦关闭就不能取数据了
// res, err := conn.Do("SET", "sssss2", 1, "NX", "EX", 300)
// fmt.Println(res, err)
lock := util.NewUserRedisLock(&gin.Context{}, "test14", util.DefaultRedisLockExpire)
fmt.Println("上锁:", lock.TryLock())
time.Sleep(time.Second * 3)
defer lock.UnLock()
}
package util
import (
"fmt"
_ "xxxx/task-flow-api/initialize"
"testing"
)
func TestGetSetInt(t *testing.T) {
res, err := GetSetInt("test", func(item RedisItemInterface) (int, error) {
item.ExpiresAfter(60)
return 1232, nil
})
if err != nil {
fmt.Println("报错,", err)
}
fmt.Println("返回:", res)
}
func TestGetSetAny(t *testing.T) {
type TestAny struct {
A int
B int
}
res, err := GetSetAny[TestAny]("TestAny", func(item RedisItemInterface) (TestAny, error) {
item.ExpiresAfter(60)
return TestAny{
A: 1,
B: 2,
}, nil
})
if err != nil {
fmt.Println("报错,", err)
}
fmt.Printf("%#v\n", res)
}