mirror of
https://github.com/gookit/cache.git
synced 2025-09-26 20:21:16 +08:00
238 lines
4.8 KiB
Go
238 lines
4.8 KiB
Go
// Package redis is a simple redis cache implement.
|
|
// base on the package: https://github.com/gomodule/redigo
|
|
package redis
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/gomodule/redigo/redis"
|
|
"github.com/gookit/cache"
|
|
)
|
|
|
|
// Name driver name
|
|
const Name = "redigo"
|
|
|
|
// RedisCache fallback alias
|
|
type RedisCache = Redigo
|
|
|
|
// Redigo driver definition.
|
|
// redigo doc link: https://pkg.go.dev/github.com/gomodule/redigo/redis#pkg-examples
|
|
type Redigo struct {
|
|
cache.BaseDriver
|
|
// redis connection pool
|
|
pool *redis.Pool
|
|
// info
|
|
url string
|
|
pwd string
|
|
dbNum int
|
|
}
|
|
|
|
// New redis cache
|
|
func New(url, pwd string, dbNum int) *Redigo {
|
|
rc := &Redigo{
|
|
url: url, pwd: pwd, dbNum: dbNum,
|
|
}
|
|
|
|
return rc
|
|
}
|
|
|
|
// Connect create and connect to redis server
|
|
func Connect(url, pwd string, dbNum int) *Redigo {
|
|
return New(url, pwd, dbNum).Connect()
|
|
}
|
|
|
|
// Connect to redis server
|
|
func (c *Redigo) Connect() *Redigo {
|
|
c.pool = newPool(c.url, c.pwd, c.dbNum)
|
|
c.Logf("connect to server %s db is %d", c.url, c.dbNum)
|
|
|
|
return c
|
|
}
|
|
|
|
/*************************************************************
|
|
* methods implements of the gsr.SimpleCacher
|
|
*************************************************************/
|
|
|
|
// Get value by key
|
|
func (c *Redigo) Get(key string) any {
|
|
bts, err := redis.Bytes(c.exec("Get", c.Key(key)))
|
|
|
|
return c.Unmarshal(bts, err)
|
|
}
|
|
|
|
// GetAs get cache and unmarshal to ptr
|
|
func (c *Redigo) GetAs(key string, ptr any) error {
|
|
bts, err := redis.Bytes(c.exec("Get", c.Key(key)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.UnmarshalTo(bts, ptr)
|
|
}
|
|
|
|
// Set value by key
|
|
func (c *Redigo) Set(key string, val any, ttl time.Duration) (err error) {
|
|
val, err = c.Marshal(val)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = c.exec("SetEx", c.Key(key), int64(ttl/time.Second), val)
|
|
return
|
|
}
|
|
|
|
// Del value by key
|
|
func (c *Redigo) Del(key string) (err error) {
|
|
_, err = c.exec("Del", c.Key(key))
|
|
return
|
|
}
|
|
|
|
// Has cache key
|
|
func (c *Redigo) Has(key string) bool {
|
|
// return 0 OR 1
|
|
one, err := redis.Int(c.exec("Exists", c.Key(key)))
|
|
c.SetLastErr(err)
|
|
|
|
return one == 1
|
|
}
|
|
|
|
// GetMulti values by keys
|
|
func (c *Redigo) GetMulti(keys []string) map[string]any {
|
|
conn := c.pool.Get()
|
|
defer conn.Close()
|
|
|
|
args := make([]any, 0, len(keys))
|
|
for _, key := range keys {
|
|
args = append(args, c.Key(key))
|
|
}
|
|
|
|
list, err := redis.Values(c.exec("MGet", args...))
|
|
if err != nil {
|
|
c.SetLastErr(err)
|
|
return nil
|
|
}
|
|
|
|
values := make(map[string]any, len(keys))
|
|
for i, val := range list {
|
|
values[keys[i]] = val
|
|
}
|
|
|
|
return values
|
|
}
|
|
|
|
// SetMulti values
|
|
func (c *Redigo) SetMulti(values map[string]any, ttl time.Duration) (err error) {
|
|
conn := c.pool.Get()
|
|
defer conn.Close()
|
|
|
|
// open multi
|
|
err = conn.Send("Multi")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ttlSec := int64(ttl / time.Second)
|
|
for key, val := range values {
|
|
// bs, _ := cache.Marshal(val)
|
|
conn.Send("SetEx", c.Key(key), ttlSec, val)
|
|
}
|
|
|
|
// do exec
|
|
_, err = redis.Ints(conn.Do("Exec"))
|
|
return
|
|
}
|
|
|
|
// DelMulti values by keys
|
|
func (c *Redigo) DelMulti(keys []string) (err error) {
|
|
args := make([]any, 0, len(keys))
|
|
for _, key := range keys {
|
|
args = append(args, c.Key(key))
|
|
}
|
|
|
|
_, err = c.exec("Del", args...)
|
|
return
|
|
}
|
|
|
|
// Close connection
|
|
func (c *Redigo) Close() error {
|
|
return c.pool.Close()
|
|
}
|
|
|
|
// Clear all caches
|
|
func (c *Redigo) Clear() error {
|
|
_, err := c.exec("FlushDb")
|
|
return err
|
|
}
|
|
|
|
/*************************************************************
|
|
* helper methods
|
|
*************************************************************/
|
|
|
|
// Pool get
|
|
func (c *Redigo) Pool() *redis.Pool {
|
|
return c.pool
|
|
}
|
|
|
|
// String get
|
|
func (c *Redigo) String() string {
|
|
pwd := "*"
|
|
if c.IsDebug() {
|
|
pwd = c.pwd
|
|
}
|
|
|
|
return fmt.Sprintf("connection info. url: %s, pwd: %s, dbNum: %d", c.url, pwd, c.dbNum)
|
|
}
|
|
|
|
// actually do the redis cmds, args[0] must be the key name.
|
|
func (c *Redigo) exec(commandName string, args ...any) (reply any, err error) {
|
|
if len(args) < 1 {
|
|
return nil, errors.New("missing required arguments")
|
|
}
|
|
|
|
conn := c.pool.Get()
|
|
defer conn.Close()
|
|
|
|
if c.IsDebug() {
|
|
st := time.Now()
|
|
reply, err = conn.Do(commandName, args...)
|
|
c.Logf(
|
|
"operate redis cache. command: %s, key: %v, elapsed time: %.03f\n",
|
|
commandName, args[0], time.Since(st).Seconds()*1000,
|
|
)
|
|
return
|
|
}
|
|
|
|
return conn.Do(commandName, args...)
|
|
}
|
|
|
|
// create new pool
|
|
func newPool(url, password string, dbNum int) *redis.Pool {
|
|
return &redis.Pool{
|
|
MaxIdle: 5,
|
|
// timeout
|
|
IdleTimeout: 240 * time.Second,
|
|
Dial: func() (redis.Conn, error) {
|
|
c, err := redis.Dial("tcp", url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if password != "" {
|
|
_, err := c.Do("AUTH", password)
|
|
if err != nil {
|
|
_ = c.Close()
|
|
return nil, err
|
|
}
|
|
}
|
|
_, _ = c.Do("SELECT", dbNum)
|
|
return c, err
|
|
},
|
|
TestOnBorrow: func(c redis.Conn, t time.Time) error {
|
|
_, err := c.Do("PING")
|
|
return err
|
|
},
|
|
}
|
|
}
|