update
This commit is contained in:
19
myredis/option.go
Normal file
19
myredis/option.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package myredis
|
||||
|
||||
import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
type Opt func(*Client)
|
||||
|
||||
func WithRedisOpt(v *redis.Options) Opt {
|
||||
return func(r *Client) {
|
||||
r.redisOpt = v
|
||||
}
|
||||
}
|
||||
|
||||
func WithDisablePing(v bool) Opt {
|
||||
return func(r *Client) {
|
||||
r.disablePing = v
|
||||
}
|
||||
}
|
||||
369
myredis/redis.go
369
myredis/redis.go
@@ -3,178 +3,245 @@ package myredis
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"git.makemake.in/kzkzzzz/mycommon/myconf"
|
||||
"github.com/google/uuid"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const DefaultKey = "default"
|
||||
const (
|
||||
DefaultInstance = "redis"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
*redis.Client
|
||||
redisOpt *redis.Options
|
||||
disablePing bool
|
||||
}
|
||||
|
||||
//type Conf struct {
|
||||
// Addr string
|
||||
// Password string
|
||||
// Db int
|
||||
// PoolSize int // 连接池数量 如果不够用会继续新建连接 可以用 MaxActiveConns 限制
|
||||
// MinIdleConn int // 最小空闲连接数 预热可能用到
|
||||
// MaxIdleConn int // 最大空闲连接数 0不限制
|
||||
// MaxActiveConn int // 0 不限制, 如果不限制, 超出pool size可以继续新建立连接, 但使用完之后不会放回连接池
|
||||
// PoolTimeout string // 等待连接池超时时间 Default is ReadTimeout + 1 second.
|
||||
// DialTimeout string // 拨号超时时间
|
||||
// ReadTimeout string // 读取超时
|
||||
// WriteTimeout string // 写入超时
|
||||
// MaxRetries int // 重试次数
|
||||
// ConnMaxIdleTime string // 连接空闲时间
|
||||
//}
|
||||
|
||||
var (
|
||||
DefaultConfig = &Config{
|
||||
Addr: "127.0.0.1:6379",
|
||||
Password: "",
|
||||
DB: 0,
|
||||
PoolSize: 16,
|
||||
MinIdleConn: 4,
|
||||
MaxConnAge: "4h",
|
||||
IdleTimeout: "15m",
|
||||
}
|
||||
|
||||
instanceMap = make(map[string]*MyRedis)
|
||||
defaultClient *redis.Client
|
||||
instanceMap = &sync.Map{}
|
||||
)
|
||||
|
||||
type (
|
||||
MyRedis struct {
|
||||
*redis.Client
|
||||
}
|
||||
|
||||
Config struct {
|
||||
Addr string
|
||||
Password string
|
||||
DB int
|
||||
PoolSize int
|
||||
MinIdleConn int
|
||||
MaxConnAge string
|
||||
IdleTimeout string
|
||||
}
|
||||
)
|
||||
|
||||
func DB(key ...string) *MyRedis {
|
||||
var key0 string
|
||||
|
||||
if len(key) > 0 {
|
||||
key0 = key[0]
|
||||
func GetClient(name ...string) *Client {
|
||||
var instanceName string
|
||||
if len(name) > 0 {
|
||||
instanceName = name[0]
|
||||
} else {
|
||||
key0 = DefaultKey
|
||||
instanceName = DefaultInstance
|
||||
}
|
||||
|
||||
instance, ok := instanceMap[key0]
|
||||
v, ok := instanceMap.Load(instanceName)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("redis %s not config", key0))
|
||||
panic(fmt.Errorf("redis instance [%s] not init", instanceName))
|
||||
}
|
||||
return instance
|
||||
|
||||
return v.(*Client)
|
||||
}
|
||||
|
||||
func InitDefault(config *Config) error {
|
||||
return Init(DefaultKey, config)
|
||||
}
|
||||
|
||||
func Init(key string, config *Config) error {
|
||||
rd, err := New(config)
|
||||
// InitClient 初始化全局默认client
|
||||
func InitClient(config *myconf.Config, opts ...Opt) {
|
||||
client, err := NewClient(DefaultInstance, config, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
panic(err)
|
||||
}
|
||||
instanceMap[key] = rd
|
||||
instanceMap.Store(DefaultInstance, client)
|
||||
}
|
||||
|
||||
// InitClientInstance 初始化全局的client
|
||||
func InitClientInstance(instanceName string, config *myconf.Config, opts ...Opt) {
|
||||
client, err := NewClient(instanceName, config, opts...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
instanceMap.Store(instanceName, client)
|
||||
}
|
||||
|
||||
func NewClient(instanceName string, config *myconf.Config, opts ...Opt) (*Client, error) {
|
||||
cf := &Conf{}
|
||||
err := config.UnmarshalKey(instanceName, &cf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := NewClientFromConf(cf, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instanceMap.Store(instanceMap, client)
|
||||
return client, nil
|
||||
}
|
||||
|
||||
type Conf struct {
|
||||
Addr string
|
||||
Password string
|
||||
Db int
|
||||
PoolSize int // 连接池数量 如果不够用会继续新建连接 可以用 MaxActiveConns 限制
|
||||
MinIdleConn int // 最小空闲连接数 预热可能用到
|
||||
MaxIdleConn int // 最大空闲连接数 0不限制
|
||||
MaxActiveConn int // 0 不限制, 如果不限制, 超出pool size可以继续新建立连接, 但使用完之后不会放回连接池
|
||||
DialTimeout string // 拨号超时时间, 时间单位格式 10ms, 60s, 15m, 2h...
|
||||
ReadTimeout string // 读取超时
|
||||
WriteTimeout string // 写入超时
|
||||
ConnMaxIdleTime string // 连接空闲时间
|
||||
PoolTimeout string // 等待连接池超时时间 Default is ReadTimeout + 1 second.
|
||||
MaxRetries int // 重试次数
|
||||
}
|
||||
|
||||
func NewClientFromConf(cf *Conf, opts ...Opt) (*Client, error) {
|
||||
c := &Client{
|
||||
redisOpt: DefaultRedisOpt(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
|
||||
err := c.parseConfToRedisOpt(cf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := redis.NewClient(c.redisOpt)
|
||||
|
||||
if c.disablePing == false {
|
||||
_, err := client.Ping(context.Background()).Result()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("redis ping err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.Client = client
|
||||
|
||||
instanceMap.Store(uuid.New().String(), c)
|
||||
|
||||
log.Printf("connect redis success [addr:%s - db:%d]", cf.Addr, cf.Db)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func DefaultRedisOpt() *redis.Options {
|
||||
return &redis.Options{
|
||||
Addr: "",
|
||||
Password: "",
|
||||
DB: 0,
|
||||
MaxRetries: 3, // 重试次数
|
||||
DialTimeout: time.Millisecond * 500, // 拨号超时时间
|
||||
ReadTimeout: time.Second, // 读取超时
|
||||
WriteTimeout: time.Second * 3, // 写入超时
|
||||
PoolSize: 1024, // 连接池数量 如果不够用会继续新建连接 可以用 MaxActiveConns 限制
|
||||
PoolTimeout: time.Second + time.Second, // 等待连接池超时时间 Default is ReadTimeout + 1 second.
|
||||
MinIdleConns: 0, // 最小空闲连接数 预热可能用到
|
||||
MaxIdleConns: 0, // 最大空闲连接数 0不限制
|
||||
MaxActiveConns: 0, // 0 不限制, 如果不限制, 超出pool size可以继续新建立连接, 但使用完之后不会放回连接池
|
||||
ConnMaxIdleTime: time.Minute * 10, // 连接空闲时间
|
||||
ContextTimeoutEnabled: true, // context控制超时用到
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) parseTime(v string) (time.Duration, error) {
|
||||
if v == "" {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
d, err := time.ParseDuration(v)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("parse time %s err: %s", v, err)
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (c *Client) parseConfToRedisOpt(cf *Conf) error {
|
||||
c.redisOpt.Addr = cf.Addr
|
||||
c.redisOpt.Password = cf.Password
|
||||
c.redisOpt.DB = cf.Db
|
||||
|
||||
if v := cf.PoolSize; v > 0 {
|
||||
c.redisOpt.PoolSize = v
|
||||
}
|
||||
|
||||
if v := cf.MinIdleConn; v > 0 {
|
||||
c.redisOpt.MinIdleConns = v
|
||||
}
|
||||
|
||||
if v := cf.MaxIdleConn; v > 0 {
|
||||
c.redisOpt.MaxIdleConns = v
|
||||
}
|
||||
|
||||
if v := cf.MaxActiveConn; v > 0 {
|
||||
c.redisOpt.MaxActiveConns = v
|
||||
}
|
||||
|
||||
if v := cf.MaxRetries; v > 0 {
|
||||
c.redisOpt.MaxRetries = v
|
||||
}
|
||||
|
||||
if v, err := c.parseTime(cf.DialTimeout); err != nil {
|
||||
return err
|
||||
} else if v > 0 {
|
||||
c.redisOpt.DialTimeout = v
|
||||
}
|
||||
|
||||
if v, err := c.parseTime(cf.ReadTimeout); err != nil {
|
||||
return err
|
||||
} else if v > 0 {
|
||||
c.redisOpt.ReadTimeout = v
|
||||
}
|
||||
|
||||
if v, err := c.parseTime(cf.WriteTimeout); err != nil {
|
||||
return err
|
||||
} else if v > 0 {
|
||||
c.redisOpt.WriteTimeout = v
|
||||
}
|
||||
|
||||
if v, err := c.parseTime(cf.ConnMaxIdleTime); err != nil {
|
||||
return err
|
||||
} else if v > 0 {
|
||||
c.redisOpt.ConnMaxIdleTime = v
|
||||
}
|
||||
|
||||
if v, err := c.parseTime(cf.PoolTimeout); err != nil {
|
||||
return err
|
||||
} else if v > 0 {
|
||||
c.redisOpt.PoolTimeout = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func New(config *Config) (*MyRedis, error) {
|
||||
var (
|
||||
maxConnAge, _ = time.ParseDuration(DefaultConfig.MaxConnAge)
|
||||
idleTimeout, _ = time.ParseDuration(DefaultConfig.IdleTimeout)
|
||||
)
|
||||
const luaSetOnce = `if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end`
|
||||
|
||||
if config.PoolSize <= 0 {
|
||||
config.MinIdleConn = DefaultConfig.PoolSize
|
||||
// SetOnce 设置一次并设置过期时间, key不存在则设置成功返回1, key已存在返回0
|
||||
func (c *Client) SetOnce(ctx context.Context, key, value string, t time.Duration) (int, error) {
|
||||
if t < time.Second {
|
||||
return 0, fmt.Errorf("time must >= 1s")
|
||||
}
|
||||
return c.Client.Eval(ctx, luaSetOnce, []string{key}, value, t).Int()
|
||||
}
|
||||
|
||||
if config.MinIdleConn <= 0 {
|
||||
config.MinIdleConn = DefaultConfig.MinIdleConn
|
||||
}
|
||||
|
||||
if config.MaxConnAge != "" {
|
||||
t, err := time.ParseDuration(config.MaxConnAge)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse MaxConnAge err: %s\n", err)
|
||||
|
||||
}
|
||||
maxConnAge = t
|
||||
}
|
||||
|
||||
if config.IdleTimeout != "" {
|
||||
t, err := time.ParseDuration(config.IdleTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse IdleTimeout err: %s\n", err)
|
||||
|
||||
}
|
||||
idleTimeout = t
|
||||
}
|
||||
|
||||
client := redis.NewClient(&redis.Options{
|
||||
Addr: config.Addr,
|
||||
Password: config.Password,
|
||||
DB: config.DB,
|
||||
PoolSize: config.PoolSize,
|
||||
MinIdleConns: config.MinIdleConn,
|
||||
MaxConnAge: maxConnAge,
|
||||
IdleTimeout: idleTimeout,
|
||||
func CloseAllClient() {
|
||||
instanceMap.Range(func(k, v any) bool {
|
||||
v.(*Client).Close()
|
||||
return true
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
|
||||
defer cancel()
|
||||
rd := &MyRedis{}
|
||||
rd.Client = client
|
||||
ping := rd.Client.Ping(ctx)
|
||||
if ping.Err() != nil {
|
||||
return nil, fmt.Errorf("connet redis err: %s", ping.Err())
|
||||
}
|
||||
|
||||
return rd, nil
|
||||
}
|
||||
|
||||
// GetSimple 通用get
|
||||
func (r *MyRedis) GetSimple(key string) (string, error) {
|
||||
ctx := context.Background()
|
||||
return r.Client.Get(ctx, key).Result()
|
||||
}
|
||||
|
||||
// SetSimple 通用set
|
||||
func (r *MyRedis) SetSimple(key string, value interface{}, t ...time.Duration) (string, error) {
|
||||
ctx := context.Background()
|
||||
var t2 time.Duration
|
||||
if len(t) > 0 {
|
||||
t2 = t[0]
|
||||
}
|
||||
return r.Client.Set(ctx, key, value, t2).Result()
|
||||
}
|
||||
|
||||
// GetJson json序列化
|
||||
func (r *MyRedis) GetJson(key string, result interface{}) error {
|
||||
ctx := context.Background()
|
||||
res, err := r.Client.Get(ctx, key).Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = jsoniter.Unmarshal(res, &result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get key:%s 反序列化json失败(-2)", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetJson json序列化set
|
||||
func (r *MyRedis) SetJson(key string, value interface{}, t ...time.Duration) (string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
var t2 time.Duration
|
||||
if len(t) > 0 {
|
||||
t2 = t[0]
|
||||
}
|
||||
v, err := jsoniter.Marshal(value)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("set key:%s 序列化json失败", key)
|
||||
}
|
||||
return r.Client.Set(ctx, key, v, t2).Result()
|
||||
}
|
||||
|
||||
func CloseDB(key string) {
|
||||
DB(key).Client.Close()
|
||||
}
|
||||
|
||||
func CloseAllDB() {
|
||||
for _, v := range instanceMap {
|
||||
v.Client.Close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package myredis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRedis(t *testing.T) {
|
||||
err := InitDefault(&Config{
|
||||
Addr: "192.168.244.128:6379",
|
||||
Password: "",
|
||||
DB: 15,
|
||||
PoolSize: 16,
|
||||
MinIdleConn: 4,
|
||||
MaxConnAge: "1h",
|
||||
IdleTimeout: "10m",
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
defer CloseAllDB()
|
||||
|
||||
set, err := DB().Set(context.Background(), "name", "qwe123", time.Minute).Result()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(set)
|
||||
|
||||
get, err := DB().Get(context.Background(), "name").Result()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(get)
|
||||
}
|
||||
Reference in New Issue
Block a user