升级redis基础库 #2
							
								
								
									
										26
									
								
								abstract/IRedisClient.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								abstract/IRedisClient.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					// Package abstract ...
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Description : abstract ...
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Author : go_developer@163.com<白茶清欢>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Date : 2024-06-18 15:09
 | 
				
			||||||
 | 
					package abstract
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/redis/go-redis/v9"
 | 
				
			||||||
 | 
						"go.uber.org/zap"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IRedisClient redis客户端定义
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Author : go_developer@163.com<白茶清欢>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Date : 15:09 2024/6/18
 | 
				
			||||||
 | 
					type IRedisClient interface {
 | 
				
			||||||
 | 
						GetRealClient(instanceFlag string) *redis.ClusterClient                    // 获取客户端连接
 | 
				
			||||||
 | 
						GetRealClientWithError(instanceFlag string) (*redis.ClusterClient, error)  // 获取带error的客户端连接
 | 
				
			||||||
 | 
						AddClient(instanceFlag string, instanceConfig *redis.ClusterOptions) error // 添加新的客户端连接
 | 
				
			||||||
 | 
						RemoveClient(instanceFlag string)                                          // 移除一个客户端连接
 | 
				
			||||||
 | 
						SetLogger(loggerInstance *zap.Logger)                                      // 设置日志实例
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										442
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										442
									
								
								client.go
									
									
									
									
									
								
							@ -8,432 +8,58 @@
 | 
				
			|||||||
package redis
 | 
					package redis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"git.zhangdeman.cn/zhangdeman/redis/abstract"
 | 
				
			||||||
	"fmt"
 | 
						"github.com/pkg/errors"
 | 
				
			||||||
	"git.zhangdeman.cn/zhangdeman/consts"
 | 
						redisClient "github.com/redis/go-redis/v9"
 | 
				
			||||||
	"git.zhangdeman.cn/zhangdeman/serialize"
 | 
					 | 
				
			||||||
	"path/filepath"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"git.zhangdeman.cn/zhangdeman/logger"
 | 
					 | 
				
			||||||
	redisInstance "github.com/go-redis/redis/v8"
 | 
					 | 
				
			||||||
	"go.uber.org/zap"
 | 
						"go.uber.org/zap"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var Client abstract.IRedisClient
 | 
				
			||||||
	// Client 连接实例
 | 
					 | 
				
			||||||
	Client ClientInterface
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InitWithCfgFile 使用配置文件初始化
 | 
					func init() {
 | 
				
			||||||
//
 | 
						Client = &OwnClient{
 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
							lock:          &sync.RWMutex{},
 | 
				
			||||||
//
 | 
							instanceTable: make(map[string]*redisClient.ClusterClient),
 | 
				
			||||||
// Date : 21:24 2022/6/22
 | 
					 | 
				
			||||||
func InitWithCfgFile(cfgFilePath string) error {
 | 
					 | 
				
			||||||
	c := &OwnClient{
 | 
					 | 
				
			||||||
		lock:           &sync.RWMutex{},
 | 
					 | 
				
			||||||
		instanceTable:  make(map[string]*RealClient),
 | 
					 | 
				
			||||||
		confTable:      make(map[string]*FullConfig),
 | 
					 | 
				
			||||||
		parseErrorFunc: defaultParseError,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := c.loadConfig(cfgFilePath); nil != err {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := c.init(); nil != err {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Client = c
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InitWithCfgDir 使用配置目录初始化
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 21:31 2022/6/22
 | 
					 | 
				
			||||||
func InitWithCfgDir(cfgDir string) error {
 | 
					 | 
				
			||||||
	c := &OwnClient{
 | 
					 | 
				
			||||||
		lock:           &sync.RWMutex{},
 | 
					 | 
				
			||||||
		instanceTable:  make(map[string]*RealClient),
 | 
					 | 
				
			||||||
		confTable:      make(map[string]*FullConfig),
 | 
					 | 
				
			||||||
		parseErrorFunc: defaultParseError,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := c.batchLoadConfig(cfgDir); nil != err {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := c.init(); nil != err {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Client = c
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// InitWithCfgFileList 使用批量的配置文件初始化
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 21:33 2022/6/22
 | 
					 | 
				
			||||||
func InitWithCfgFileList(cfgFileList []string) error {
 | 
					 | 
				
			||||||
	c := &OwnClient{
 | 
					 | 
				
			||||||
		lock:           &sync.RWMutex{},
 | 
					 | 
				
			||||||
		instanceTable:  make(map[string]*RealClient),
 | 
					 | 
				
			||||||
		confTable:      make(map[string]*FullConfig),
 | 
					 | 
				
			||||||
		parseErrorFunc: defaultParseError,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, cfgFilePath := range cfgFileList {
 | 
					 | 
				
			||||||
		if err := c.loadConfig(cfgFilePath); nil != err {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err := c.init(); nil != err {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	Client = c
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// defaultParseError ...
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 10:59 下午 2021/2/27
 | 
					 | 
				
			||||||
func defaultParseError(err error) error {
 | 
					 | 
				
			||||||
	if nil == err {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	errMsg := err.Error()
 | 
					 | 
				
			||||||
	if errMsg == "nil" || errMsg == "<nil>" {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	strArr := strings.Split(errMsg, ":")
 | 
					 | 
				
			||||||
	if len(strArr) != 2 {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	msg := strings.ToLower(strings.TrimSpace(strArr[1]))
 | 
					 | 
				
			||||||
	if msg == "nil" || msg == "<nil>" {
 | 
					 | 
				
			||||||
		return nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RealClient 包装好的 redis client
 | 
					 | 
				
			||||||
type RealClient struct {
 | 
					 | 
				
			||||||
	Flag   string                // redis 标识
 | 
					 | 
				
			||||||
	Master *redisInstance.Client // redis 实例
 | 
					 | 
				
			||||||
	Slave  *redisInstance.Client // redis 实例
 | 
					 | 
				
			||||||
	Logger *zap.Logger           // 日志实例
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewClient 获取redis client实例
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 5:05 下午 2021/2/27
 | 
					 | 
				
			||||||
func NewClient(config map[string]*FullConfig, parseErrorFunc func(err error) error) (ClientInterface, error) {
 | 
					 | 
				
			||||||
	c := &OwnClient{
 | 
					 | 
				
			||||||
		lock:           &sync.RWMutex{},
 | 
					 | 
				
			||||||
		instanceTable:  make(map[string]*RealClient),
 | 
					 | 
				
			||||||
		loggerTable:    make(map[string]*zap.Logger),
 | 
					 | 
				
			||||||
		confTable:      config,
 | 
					 | 
				
			||||||
		parseErrorFunc: parseErrorFunc,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if nil == c.parseErrorFunc {
 | 
					 | 
				
			||||||
		c.parseErrorFunc = defaultParseError
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return c, c.init()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// OwnClient 包装的redis client
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 4:52 下午 2021/2/27
 | 
					 | 
				
			||||||
type OwnClient struct {
 | 
					type OwnClient struct {
 | 
				
			||||||
	lock           *sync.RWMutex          // 锁
 | 
						lock          *sync.RWMutex
 | 
				
			||||||
	loggerTable    map[string]*zap.Logger // 日志实例
 | 
						instanceTable map[string]*redisClient.ClusterClient
 | 
				
			||||||
	instanceTable  map[string]*RealClient // redis 实例
 | 
						logger        *zap.Logger
 | 
				
			||||||
	confTable      map[string]*FullConfig // redis 配置
 | 
					 | 
				
			||||||
	parseErrorFunc func(err error) error  // 解析err的function,解析执行结果是否为失败,有的场景,执行成功,返回 redis:nil / redis:<nil>
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddClientWithCfgFile 使用具体配置文件初始化
 | 
					func (o *OwnClient) GetRealClient(instanceFlag string) *redisClient.ClusterClient {
 | 
				
			||||||
//
 | 
						o.lock.RLock()
 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
						defer o.lock.RUnlock()
 | 
				
			||||||
//
 | 
						return o.instanceTable[instanceFlag]
 | 
				
			||||||
// Date : 20:57 2022/6/15
 | 
					 | 
				
			||||||
func (c *OwnClient) AddClientWithCfgFile(cfgPath string) error {
 | 
					 | 
				
			||||||
	var (
 | 
					 | 
				
			||||||
		err error
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err = c.loadConfig(cfgPath); nil != err {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return c.init()
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddClientWithCfgDir 使用配置目录进行初始化
 | 
					func (o *OwnClient) GetRealClientWithError(instanceFlag string) (*redisClient.ClusterClient, error) {
 | 
				
			||||||
//
 | 
						o.lock.RLock()
 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
						defer o.lock.RUnlock()
 | 
				
			||||||
//
 | 
						instance, exist := o.instanceTable[instanceFlag]
 | 
				
			||||||
// Date : 20:58 2022/6/15
 | 
					 | 
				
			||||||
func (c *OwnClient) AddClientWithCfgDir(cfgDir string) error {
 | 
					 | 
				
			||||||
	var (
 | 
					 | 
				
			||||||
		err error
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if err = c.batchLoadConfig(cfgDir); nil != err {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return c.init()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AddClientWithCfg 通过配置增加实例
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 11:45 2023/8/16
 | 
					 | 
				
			||||||
func (c *OwnClient) AddClientWithCfg(flag string, cfg *FullConfig) error {
 | 
					 | 
				
			||||||
	c.lock.Lock()
 | 
					 | 
				
			||||||
	c.confTable[flag] = cfg
 | 
					 | 
				
			||||||
	c.lock.Unlock()
 | 
					 | 
				
			||||||
	return c.init()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// loadConfig 载入配置文件
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 14:31 2022/6/15
 | 
					 | 
				
			||||||
func (c *OwnClient) loadConfig(cfgPath string) error {
 | 
					 | 
				
			||||||
	var (
 | 
					 | 
				
			||||||
		err error
 | 
					 | 
				
			||||||
		cfg FullConfig
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	filePathArr := strings.Split(cfgPath, string(filepath.Separator))
 | 
					 | 
				
			||||||
	if len(filePathArr) == 0 {
 | 
					 | 
				
			||||||
		return CfgFilePathError()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fileName := strings.ToLower(filePathArr[len(filePathArr)-1])
 | 
					 | 
				
			||||||
	fileArr := strings.Split(fileName, ".")
 | 
					 | 
				
			||||||
	if len(filePathArr) < 2 {
 | 
					 | 
				
			||||||
		return CfgFileFormatErr("未知")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	flag := strings.Trim(
 | 
					 | 
				
			||||||
		strings.Trim(
 | 
					 | 
				
			||||||
			strings.Trim(fileName, ".json"),
 | 
					 | 
				
			||||||
			".yaml"), ".yml")
 | 
					 | 
				
			||||||
	switch strings.ToLower(fileArr[len(fileArr)-1]) {
 | 
					 | 
				
			||||||
	case consts.FileTypeJson:
 | 
					 | 
				
			||||||
		if err = serialize.File.ReadJSONContent(cfgPath, &cfg); nil != err {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		flag = strings.Join(fileArr[0:len(fileArr)-1], ".")
 | 
					 | 
				
			||||||
	case consts.FileTypeYml:
 | 
					 | 
				
			||||||
		fallthrough
 | 
					 | 
				
			||||||
	case consts.FileTypeYaml:
 | 
					 | 
				
			||||||
		if err = serialize.File.ReadYmlContent(cfgPath, &cfg); nil != err {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		flag = strings.Join(fileArr[0:len(fileArr)-1], ".")
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		// 不支持的格式,跳过
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	c.lock.Lock()
 | 
					 | 
				
			||||||
	c.confTable[flag] = &cfg
 | 
					 | 
				
			||||||
	c.lock.Unlock()
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// batchLoadConfig 批量载入配置
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 16:44 2022/6/15
 | 
					 | 
				
			||||||
func (c *OwnClient) batchLoadConfig(cfgDir string) error {
 | 
					 | 
				
			||||||
	filepathNames, _ := filepath.Glob(filepath.Join(cfgDir, "*"))
 | 
					 | 
				
			||||||
	for i := range filepathNames {
 | 
					 | 
				
			||||||
		if err := c.loadConfig(filepathNames[i]); nil != err {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// init 初始化redis连接
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 5:31 下午 2021/2/27
 | 
					 | 
				
			||||||
func (c *OwnClient) init() error {
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	for flag, conf := range c.confTable {
 | 
					 | 
				
			||||||
		if _, exist := c.instanceTable[flag]; exist {
 | 
					 | 
				
			||||||
			// 实例已经初始化
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		c.instanceTable[flag] = &RealClient{
 | 
					 | 
				
			||||||
			Flag:   flag,
 | 
					 | 
				
			||||||
			Master: redisInstance.NewClient(Config2Options(conf.Master)),
 | 
					 | 
				
			||||||
			Slave:  redisInstance.NewClient(Config2Options(conf.Slave)),
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if c.instanceTable[flag].Logger, err = logger.GetLogInstanceFromInputConfig(conf.Logger); nil != err {
 | 
					 | 
				
			||||||
			return InitLoggerErr(flag, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// initLogger 初始化日志
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 7:07 下午 2021/2/27
 | 
					 | 
				
			||||||
func (c *OwnClient) initLogger(flag string, conf *logger.InputLogConfig) error {
 | 
					 | 
				
			||||||
	var err error
 | 
					 | 
				
			||||||
	c.loggerTable[flag], err = logger.GetLogInstanceFromInputConfig(conf)
 | 
					 | 
				
			||||||
	return LoggerInitFail(flag, err)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetRedisClientWithError 获取redis实例
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 5:16 下午 2021/2/27
 | 
					 | 
				
			||||||
func (c *OwnClient) GetRedisClientWithError(flag string) (*RealClient, error) {
 | 
					 | 
				
			||||||
	c.lock.RLock()
 | 
					 | 
				
			||||||
	defer c.lock.RUnlock()
 | 
					 | 
				
			||||||
	redisClient, exist := c.instanceTable[flag]
 | 
					 | 
				
			||||||
	if !exist {
 | 
						if !exist {
 | 
				
			||||||
		return nil, FlagNotFound(flag)
 | 
							return nil, errors.New(instanceFlag + " : redis instance is not found")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return redisClient, nil
 | 
						return instance, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetRedisClient ...
 | 
					func (o *OwnClient) AddClient(instanceFlag string, instanceConfig *redisClient.ClusterOptions) error {
 | 
				
			||||||
//
 | 
						instance := redisClient.NewClusterClient(instanceConfig)
 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
						o.lock.Lock()
 | 
				
			||||||
//
 | 
						defer o.lock.Unlock()
 | 
				
			||||||
// Date : 11:52 2023/8/16
 | 
						o.instanceTable[instanceFlag] = instance
 | 
				
			||||||
func (c *OwnClient) GetRedisClient(flag string) *RealClient {
 | 
						return nil
 | 
				
			||||||
	redisClient, _ := c.GetRedisClientWithError(flag)
 | 
					 | 
				
			||||||
	return redisClient
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// log 记录redis请求日志
 | 
					func (o *OwnClient) RemoveClient(instanceFlag string) {
 | 
				
			||||||
//
 | 
						o.lock.Lock()
 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
						defer o.lock.Unlock()
 | 
				
			||||||
//
 | 
						delete(o.instanceTable, instanceFlag)
 | 
				
			||||||
// Date : 8:52 下午 2021/2/27
 | 
					 | 
				
			||||||
func (c *OwnClient) log(ctx *Context, realClient *RealClient, isMaster bool, cmdResult redisInstance.Cmder, startTime int64, finishTime int64) {
 | 
					 | 
				
			||||||
	if nil == realClient || nil == realClient.Logger {
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	realClient.Logger.Info(
 | 
					 | 
				
			||||||
		"执行redis命令日志记录",
 | 
					 | 
				
			||||||
		zap.Any(ctx.RequestIDField, ctx.RequestID),                       // 上下文串联的requestID
 | 
					 | 
				
			||||||
		zap.String("instance_flag", ctx.Flag),                            // redis 实例的标识
 | 
					 | 
				
			||||||
		zap.Any("exec_command_name", cmdResult.Name()),                   // 执行的命令
 | 
					 | 
				
			||||||
		zap.Any("exec_command_arg", cmdResult.Args()),                    // 执行的命令参数
 | 
					 | 
				
			||||||
		zap.Bool("use_master", isMaster),                                 // 是否使用主节点
 | 
					 | 
				
			||||||
		zap.Float64("exec_used_time", float64(finishTime-startTime)/1e6), // 耗时,单位: ms
 | 
					 | 
				
			||||||
		zap.Error(cmdResult.Err()),                                       // 异常信息
 | 
					 | 
				
			||||||
		zap.String("command_result", cmdResult.String()),                 // 命令执行后的结果
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CommandProxy 执行命令的代理
 | 
					func (o *OwnClient) SetLogger(loggerInstance *zap.Logger) {
 | 
				
			||||||
//
 | 
						o.logger = loggerInstance
 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 9:41 下午 2021/2/27
 | 
					 | 
				
			||||||
func (c *OwnClient) CommandProxy(ctx *Context, flag string, cmd string, param ...interface{}) (string, error) {
 | 
					 | 
				
			||||||
	var (
 | 
					 | 
				
			||||||
		realClient *RealClient
 | 
					 | 
				
			||||||
		err        error
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
	if len(cmd) == 0 {
 | 
					 | 
				
			||||||
		return "", EmptyCmd()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if nil == ctx {
 | 
					 | 
				
			||||||
		ctx = NewContext(flag)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if realClient, err = c.GetRedisClientWithError(ctx.Flag); nil != err {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	flagArr := strings.Split(flag, "#")
 | 
					 | 
				
			||||||
	if len(flagArr) == 1 {
 | 
					 | 
				
			||||||
		flagArr = append(flagArr, "r")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	isMater := flagArr[1] == "w"
 | 
					 | 
				
			||||||
	redisClient := realClient.Slave
 | 
					 | 
				
			||||||
	if isMater {
 | 
					 | 
				
			||||||
		redisClient = realClient.Master
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	redisCmd := append([]interface{}{cmd}, param...)
 | 
					 | 
				
			||||||
	startTime := time.Now().Unix()
 | 
					 | 
				
			||||||
	cmdResult := redisClient.Do(ctx.Ctx, redisCmd...)
 | 
					 | 
				
			||||||
	go c.log(ctx, realClient, isMater, cmdResult, startTime, time.Now().UnixNano())
 | 
					 | 
				
			||||||
	return fmt.Sprintf("%v", cmdResult.Val()), c.parseErrorFunc(cmdResult.Err())
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// CommandProxyWithReceiver 执行命令,并解析结果
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 10:00 下午 2021/2/27
 | 
					 | 
				
			||||||
func (c *OwnClient) CommandProxyWithReceiver(ctx *Context, flag string, receiver interface{}, cmd string, param ...interface{}) error {
 | 
					 | 
				
			||||||
	if nil == receiver {
 | 
					 | 
				
			||||||
		return ReceiverISNIL()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	var (
 | 
					 | 
				
			||||||
		err    error
 | 
					 | 
				
			||||||
		result string
 | 
					 | 
				
			||||||
	)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if result, err = c.CommandProxy(ctx, flag, cmd, param...); nil != err {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ResultConvertFail(json.Unmarshal([]byte(result), receiver))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RemoveClient 移除client
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 11:49 2023/8/16
 | 
					 | 
				
			||||||
func (c *OwnClient) RemoveClient(flag string) {
 | 
					 | 
				
			||||||
	c.lock.Lock()
 | 
					 | 
				
			||||||
	defer c.lock.Unlock()
 | 
					 | 
				
			||||||
	delete(c.confTable, flag)
 | 
					 | 
				
			||||||
	if _, exist := c.instanceTable[flag]; exist {
 | 
					 | 
				
			||||||
		if nil != c.instanceTable[flag].Master {
 | 
					 | 
				
			||||||
			_ = c.instanceTable[flag].Master.Close()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if nil != c.instanceTable[flag].Slave {
 | 
					 | 
				
			||||||
			_ = c.instanceTable[flag].Slave.Close()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	delete(c.instanceTable, flag)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ClientInterface 定义redis client的接口实现,方便单元测试数据mock
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 10:49 下午 2021/2/27
 | 
					 | 
				
			||||||
type ClientInterface interface {
 | 
					 | 
				
			||||||
	GetRedisClient(flag string) *RealClient
 | 
					 | 
				
			||||||
	GetRedisClientWithError(flag string) (*RealClient, error)
 | 
					 | 
				
			||||||
	CommandProxy(ctx *Context, flag string, cmd string, param ...interface{}) (string, error)
 | 
					 | 
				
			||||||
	CommandProxyWithReceiver(ctx *Context, flag string, receiver interface{}, cmd string, param ...interface{}) error
 | 
					 | 
				
			||||||
	AddClientWithCfgFile(cfgPath string) error
 | 
					 | 
				
			||||||
	AddClientWithCfgDir(cfgDir string) error
 | 
					 | 
				
			||||||
	AddClientWithCfg(flag string, cfg *FullConfig) error
 | 
					 | 
				
			||||||
	RemoveClient(flag string)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										53
									
								
								define/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								define/config.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					// Package define ...
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Description : define ...
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Author : go_developer@163.com<白茶清欢>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Date : 2024-06-18 16:12
 | 
				
			||||||
 | 
					package define
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ClusterOptions struct {
 | 
				
			||||||
 | 
						// A seed list of host:port addresses of cluster nodes.
 | 
				
			||||||
 | 
						Addrs []string `json:"addrs" yaml:"addrs" ini:"addrs" toml:"addrs"` // 集群IP列表
 | 
				
			||||||
 | 
						// ClientName will execute the `CLIENT SETNAME ClientName` command for each conn.
 | 
				
			||||||
 | 
						ClientName string `json:"client_name" yaml:"client_name" ini:"client_name" toml:"client_name"` // 集群名称
 | 
				
			||||||
 | 
						// The maximum number of retries before giving up. Command is retried
 | 
				
			||||||
 | 
						// on network errors and MOVED/ASK redirects.
 | 
				
			||||||
 | 
						// Default is 3 retries.
 | 
				
			||||||
 | 
						MaxRedirects int `json:"max_redirects" yaml:"max_redirects" ini:"max_redirects" toml:"max_redirects"` // 最大重试次数
 | 
				
			||||||
 | 
						// Enables read-only commands on slave nodes.
 | 
				
			||||||
 | 
						ReadOnly bool `json:"read_only" yaml:"read_only" ini:"read_only" toml:"read_only"` // 只读
 | 
				
			||||||
 | 
						// Allows routing read-only commands to the closest master or slave node.
 | 
				
			||||||
 | 
						// It automatically enables ReadOnly.
 | 
				
			||||||
 | 
						RouteByLatency bool `json:"route_by_latency" yaml:"route_by_latency" ini:"route_by_latency" toml:"route_by_latency"`
 | 
				
			||||||
 | 
						// Allows routing read-only commands to the random master or slave node.
 | 
				
			||||||
 | 
						// It automatically enables ReadOnly.
 | 
				
			||||||
 | 
						RouteRandomly bool `json:"route_randomly" yaml:"route_randomly" ini:"route_randomly" toml:"route_randomly"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Protocol int
 | 
				
			||||||
 | 
						Username string `json:"username" yaml:"username" ini:"username" toml:"username"`
 | 
				
			||||||
 | 
						Password string `json:"password" yaml:"password" ini:"password" toml:"password"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						MaxRetries      int `json:"max_retries" yaml:"max_retries" ini:"max_retries" toml:"max_retries"`
 | 
				
			||||||
 | 
						MinRetryBackoff int `json:"min_retry_backoff" yaml:"min_retry_backoff" ini:"min_retry_backoff" toml:"min_retry_backoff"` // 最小重试间隔,单位 : ms
 | 
				
			||||||
 | 
						MaxRetryBackoff int `json:"max_retry_backoff" yaml:"max_retry_backoff" ini:"max_retry_backoff" toml:"max_retry_backoff"` // 最大重试时间间隔, 单位 : ms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DialTimeout           int  `json:"dial_timeout" yaml:"dial_timeout" ini:"dial_timeout" toml:"dial_timeout"`     // 连接超时时间
 | 
				
			||||||
 | 
						ReadTimeout           int  `json:"read_timeout" yaml:"read_timeout" ini:"read_timeout" toml:"read_timeout"`     // 读取超时时间
 | 
				
			||||||
 | 
						WriteTimeout          int  `json:"write_timeout" yaml:"write_timeout" ini:"write_timeout" toml:"write_timeout"` // 写入超时时间
 | 
				
			||||||
 | 
						ContextTimeoutEnabled bool `json:"context_timeout_enabled" yaml:"context_timeout_enabled" ini:"context_timeout_enabled" toml:"context_timeout_enabled"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PoolFIFO        bool `json:"pool_fifo" yaml:"pool_fifo" ini:"pool_fifo" toml:"pool_fifo"`
 | 
				
			||||||
 | 
						PoolSize        int  `json:"pool_size" yaml:"pool_size" ini:"pool_size" toml:"pool_size"`                                     // applies per cluster node and not for the whole cluster
 | 
				
			||||||
 | 
						PoolTimeout     int  `json:"pool_timeout" yaml:"pool_timeout" ini:"pool_timeout" toml:"pool_timeout"`                         // 单位 : ms
 | 
				
			||||||
 | 
						MinIdleConn     int  `json:"min_idle_conn" yaml:"min_idle_conn" ini:"min_idle_conn" toml:"min_idle_conn"`                     // 最小空闲连接数
 | 
				
			||||||
 | 
						MaxIdleConn     int  `json:"max_idle_conn" yaml:"max_idle_conn" ini:"max_idle_conn" toml:"max_idle_conn"`                     // 最大空闲连接数
 | 
				
			||||||
 | 
						MaxActiveConn   int  `json:"max_active_conn" yaml:"max_active_conn" ini:"max_active_conn" toml:"max_active_conn"`             // applies per cluster node and not for the whole cluster
 | 
				
			||||||
 | 
						ConnMaxIdleTime int  `json:"conn_max_idle_time" yaml:"conn_max_idle_time" ini:"conn_max_idle_time" toml:"conn_max_idle_time"` // 连接最大空闲时长, 单位 : s
 | 
				
			||||||
 | 
						ConnMaxLifetime int  `json:"conn_max_lifetime" yaml:"conn_max_lifetime" ini:"conn_max_lifetime" toml:"conn_max_lifetime"`     // 连接最大存活时长, 单位ms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DisableIdentity bool `json:"disable_identity" yaml:"disable_identity" ini:"disable_identity" toml:"disable_identity"` // Disable set-lib on connect. Default is false.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						IdentitySuffix string `json:"identity_suffix" yaml:"identity_suffix" ini:"identity_suffix" toml:"identity_suffix"` // Add suffix to client name. Default is empty.
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							@ -46,6 +46,7 @@ require (
 | 
				
			|||||||
	github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
						github.com/modern-go/reflect2 v1.0.2 // indirect
 | 
				
			||||||
	github.com/mozillazg/go-pinyin v0.20.0 // indirect
 | 
						github.com/mozillazg/go-pinyin v0.20.0 // indirect
 | 
				
			||||||
	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
 | 
						github.com/pelletier/go-toml/v2 v2.2.2 // indirect
 | 
				
			||||||
 | 
						github.com/redis/go-redis/v9 v9.5.3 // indirect
 | 
				
			||||||
	github.com/spaolacci/murmur3 v1.1.0 // indirect
 | 
						github.com/spaolacci/murmur3 v1.1.0 // indirect
 | 
				
			||||||
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 | 
						github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 | 
				
			||||||
	github.com/ugorji/go/codec v1.2.12 // indirect
 | 
						github.com/ugorji/go/codec v1.2.12 // indirect
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							@ -161,6 +161,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			|||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
 | 
					github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
 | 
				
			||||||
 | 
					github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
 | 
				
			||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
 | 
					github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
 | 
				
			||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
					github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										39
									
								
								logger.go
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								logger.go
									
									
									
									
									
								
							@ -1,39 +0,0 @@
 | 
				
			|||||||
// Package redis...
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Description : redis...
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 2021-02-27 5:26 下午
 | 
					 | 
				
			||||||
package redis
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"git.zhangdeman.cn/zhangdeman/logger"
 | 
					 | 
				
			||||||
	"go.uber.org/zap/zapcore"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LoggerConfig 日志配置
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 5:26 下午 2021/2/27
 | 
					 | 
				
			||||||
type LoggerConfig struct {
 | 
					 | 
				
			||||||
	LoggerPath    string
 | 
					 | 
				
			||||||
	LoggerFile    string
 | 
					 | 
				
			||||||
	LoggerLevel   zapcore.Level
 | 
					 | 
				
			||||||
	ConsoleOutput bool
 | 
					 | 
				
			||||||
	Encoder       zapcore.Encoder
 | 
					 | 
				
			||||||
	SplitConfig   *logger.RotateLogConfig
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// LogFieldConfig 日志字段配置
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Author : go_developer@163.com<白茶清欢>
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
// Date : 9:20 下午 2021/2/27
 | 
					 | 
				
			||||||
type LogFieldConfig struct {
 | 
					 | 
				
			||||||
	Message       string
 | 
					 | 
				
			||||||
	UsedTimeField string
 | 
					 | 
				
			||||||
	CommandField  string
 | 
					 | 
				
			||||||
	FlagField     string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user