redis client自适应读写命令选择主库 OR 读库 #4
@ -11,7 +11,6 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.zhangdeman.cn/zhangdeman/redis/define"
 | 
						"git.zhangdeman.cn/zhangdeman/redis/define"
 | 
				
			||||||
	"github.com/redis/go-redis/v9"
 | 
					 | 
				
			||||||
	"go.uber.org/zap"
 | 
						"go.uber.org/zap"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -21,9 +20,9 @@ import (
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// Date : 15:09 2024/6/18
 | 
					// Date : 15:09 2024/6/18
 | 
				
			||||||
type IRedisClient interface {
 | 
					type IRedisClient interface {
 | 
				
			||||||
	GetRealClient(instanceFlag string) *redis.Client                                                // 获取客户端连接
 | 
						GetRealClient(instanceFlag string) *define.ClientInfo                                           // 获取客户端连接
 | 
				
			||||||
	GetRealClientWithError(instanceFlag string) (*redis.Client, error)                              // 获取带error的客户端连接
 | 
						GetRealClientWithError(instanceFlag string) (*define.ClientInfo, error)                         // 获取带error的客户端连接
 | 
				
			||||||
	AddClient(instanceFlag string, instanceConfig *define.Options) error                            // 添加新的客户端连接
 | 
						AddClient(instanceFlag string, instanceConfig *define.Config) error                             // 添加新的客户端连接
 | 
				
			||||||
	RemoveClient(instanceFlag string)                                                               // 移除一个客户端连接
 | 
						RemoveClient(instanceFlag string)                                                               // 移除一个客户端连接
 | 
				
			||||||
	SetLogger(loggerInstance *zap.Logger, extraLogFieldList []string)                               // 设置日志实例, 全局生效, 而非针对某一个实例
 | 
						SetLogger(loggerInstance *zap.Logger, extraLogFieldList []string)                               // 设置日志实例, 全局生效, 而非针对某一个实例
 | 
				
			||||||
	Exec(ctx context.Context, instanceFlag string, command string, args ...any) *define.RedisResult // 执行任意命令
 | 
						Exec(ctx context.Context, instanceFlag string, command string, args ...any) *define.RedisResult // 执行任意命令
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										94
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								client.go
									
									
									
									
									
								
							@ -28,14 +28,14 @@ var (
 | 
				
			|||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	Client = &OwnClient{
 | 
						Client = &OwnClient{
 | 
				
			||||||
		lock:              &sync.RWMutex{},
 | 
							lock:              &sync.RWMutex{},
 | 
				
			||||||
		instanceTable:     make(map[string]*redisClient.Client),
 | 
							instanceTable:     make(map[string]*define.ClientInfo),
 | 
				
			||||||
		whiteCommandTable: make(map[string]bool),
 | 
							whiteCommandTable: make(map[string]bool),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type OwnClient struct {
 | 
					type OwnClient struct {
 | 
				
			||||||
	lock              *sync.RWMutex
 | 
						lock              *sync.RWMutex
 | 
				
			||||||
	instanceTable     map[string]*redisClient.Client
 | 
						instanceTable     map[string]*define.ClientInfo
 | 
				
			||||||
	whiteCommandTable map[string]bool
 | 
						whiteCommandTable map[string]bool
 | 
				
			||||||
	logger            *zap.Logger
 | 
						logger            *zap.Logger
 | 
				
			||||||
	extraLogFieldList []string
 | 
						extraLogFieldList []string
 | 
				
			||||||
@ -66,7 +66,7 @@ func (o *OwnClient) Exec(ctx context.Context, instanceFlag string, command strin
 | 
				
			|||||||
		ctx = context.Background()
 | 
							ctx = context.Background()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		instance *redisClient.Client
 | 
							instance *define.ClientInfo
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmdParamList := []any{
 | 
						cmdParamList := []any{
 | 
				
			||||||
@ -117,7 +117,21 @@ func (o *OwnClient) Exec(ctx context.Context, instanceFlag string, command strin
 | 
				
			|||||||
	if nil == ctx {
 | 
						if nil == ctx {
 | 
				
			||||||
		ctx = context.Background()
 | 
							ctx = context.Background()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cmdRes := instance.Do(ctx, cmdParamList...)
 | 
						if instance.ReadOnly && o.isWriteCommand(command) {
 | 
				
			||||||
 | 
							// 只读实例, 尝试执行写命令
 | 
				
			||||||
 | 
							res.Err = errors.New(instanceFlag + " : instance is read only")
 | 
				
			||||||
 | 
							return res
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if instance.ReadOnly && o.isWriteCommand(command) && instance.MasterClient() == nil {
 | 
				
			||||||
 | 
							// 写命令, 没有主库连接
 | 
				
			||||||
 | 
							res.Err = errors.New(instanceFlag + " : instance master client is nil")
 | 
				
			||||||
 | 
							return res
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						redisRealClient := instance.MasterClient()
 | 
				
			||||||
 | 
						if !o.isWriteCommand(command) {
 | 
				
			||||||
 | 
							redisRealClient = instance.SlaveClient()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cmdRes := redisRealClient.Do(ctx, cmdParamList...)
 | 
				
			||||||
	if res.Err = cmdRes.Err(); nil != res.Err {
 | 
						if res.Err = cmdRes.Err(); nil != res.Err {
 | 
				
			||||||
		return res
 | 
							return res
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -138,13 +152,13 @@ func (o *OwnClient) SetCommandWhiteList(commandList []string) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o *OwnClient) GetRealClient(instanceFlag string) *redisClient.Client {
 | 
					func (o *OwnClient) GetRealClient(instanceFlag string) *define.ClientInfo {
 | 
				
			||||||
	o.lock.RLock()
 | 
						o.lock.RLock()
 | 
				
			||||||
	defer o.lock.RUnlock()
 | 
						defer o.lock.RUnlock()
 | 
				
			||||||
	return o.instanceTable[instanceFlag]
 | 
						return o.instanceTable[instanceFlag]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o *OwnClient) GetRealClientWithError(instanceFlag string) (*redisClient.Client, error) {
 | 
					func (o *OwnClient) GetRealClientWithError(instanceFlag string) (*define.ClientInfo, error) {
 | 
				
			||||||
	o.lock.RLock()
 | 
						o.lock.RLock()
 | 
				
			||||||
	defer o.lock.RUnlock()
 | 
						defer o.lock.RUnlock()
 | 
				
			||||||
	instance, exist := o.instanceTable[instanceFlag]
 | 
						instance, exist := o.instanceTable[instanceFlag]
 | 
				
			||||||
@ -154,8 +168,57 @@ func (o *OwnClient) GetRealClientWithError(instanceFlag string) (*redisClient.Cl
 | 
				
			|||||||
	return instance, nil
 | 
						return instance, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (o *OwnClient) AddClient(instanceFlag string, instanceConfig *define.Options) error {
 | 
					func (o *OwnClient) AddClient(instanceFlag string, instanceConfig *define.Config) error {
 | 
				
			||||||
	instance := redisClient.NewClient(&redisClient.Options{
 | 
						if nil == instanceConfig.Master && !instanceConfig.ReadOnly {
 | 
				
			||||||
 | 
							// 不是只读, 则要求 主库配置 和 从库配置都要存在
 | 
				
			||||||
 | 
							return errors.New(instanceFlag + " : master config is nil")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clientInfo := &define.ClientInfo{
 | 
				
			||||||
 | 
							ReadOnly: instanceConfig.ReadOnly,
 | 
				
			||||||
 | 
							Master:   nil,
 | 
				
			||||||
 | 
							Slave:    nil,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if nil != instanceConfig.Master {
 | 
				
			||||||
 | 
							clientInfo.Master = o.newClient(instanceConfig.Master)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if nil != instanceConfig.Slave {
 | 
				
			||||||
 | 
							clientInfo.Master = o.newClient(instanceConfig.Slave)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						o.lock.Lock()
 | 
				
			||||||
 | 
						defer o.lock.Unlock()
 | 
				
			||||||
 | 
						o.instanceTable[instanceFlag] = clientInfo
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *OwnClient) RemoveClient(instanceFlag string) {
 | 
				
			||||||
 | 
						o.lock.Lock()
 | 
				
			||||||
 | 
						defer o.lock.Unlock()
 | 
				
			||||||
 | 
						delete(o.instanceTable, instanceFlag)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (o *OwnClient) SetLogger(loggerInstance *zap.Logger, extraLogFieldList []string) {
 | 
				
			||||||
 | 
						o.logger = loggerInstance
 | 
				
			||||||
 | 
						o.extraLogFieldList = extraLogFieldList
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// isWriteCommand 判断是否写命令
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Author : go_developer@163.com<白茶清欢>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Date : 16:22 2024/10/8
 | 
				
			||||||
 | 
					func (o *OwnClient) isWriteCommand(command string) bool {
 | 
				
			||||||
 | 
						return wrapperOperate.ArrayType([]string{}).Has(strings.ToUpper(command)) >= 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// newClient 获取客户端连接
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Author : go_developer@163.com<白茶清欢>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Date : 16:12 2024/10/8
 | 
				
			||||||
 | 
					func (o *OwnClient) newClient(instanceConfig *define.Options) *redisClient.Client {
 | 
				
			||||||
 | 
						return redisClient.NewClient(&redisClient.Options{
 | 
				
			||||||
		DB:         instanceConfig.DB,
 | 
							DB:         instanceConfig.DB,
 | 
				
			||||||
		Addr:       instanceConfig.Addr,
 | 
							Addr:       instanceConfig.Addr,
 | 
				
			||||||
		ClientName: instanceConfig.ClientName,
 | 
							ClientName: instanceConfig.ClientName,
 | 
				
			||||||
@ -187,19 +250,4 @@ func (o *OwnClient) AddClient(instanceFlag string, instanceConfig *define.Option
 | 
				
			|||||||
		// DisableIndentity:           instanceConfig.DisableIdentity,
 | 
							// DisableIndentity:           instanceConfig.DisableIdentity,
 | 
				
			||||||
		// IdentitySuffix:             instanceConfig.IdentitySuffix,
 | 
							// IdentitySuffix:             instanceConfig.IdentitySuffix,
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	o.lock.Lock()
 | 
					 | 
				
			||||||
	defer o.lock.Unlock()
 | 
					 | 
				
			||||||
	o.instanceTable[instanceFlag] = instance
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (o *OwnClient) RemoveClient(instanceFlag string) {
 | 
					 | 
				
			||||||
	o.lock.Lock()
 | 
					 | 
				
			||||||
	defer o.lock.Unlock()
 | 
					 | 
				
			||||||
	delete(o.instanceTable, instanceFlag)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (o *OwnClient) SetLogger(loggerInstance *zap.Logger, extraLogFieldList []string) {
 | 
					 | 
				
			||||||
	o.logger = loggerInstance
 | 
					 | 
				
			||||||
	o.extraLogFieldList = extraLogFieldList
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,8 @@
 | 
				
			|||||||
// Date : 2024-06-18 16:12
 | 
					// Date : 2024-06-18 16:12
 | 
				
			||||||
package define
 | 
					package define
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import redisClient "github.com/redis/go-redis/v9"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Options struct {
 | 
					type Options struct {
 | 
				
			||||||
	DB      int    `json:"db" yaml:"db" ini:"db" toml:"db"`                     // 选择的数据库序号
 | 
						DB      int    `json:"db" yaml:"db" ini:"db" toml:"db"`                     // 选择的数据库序号
 | 
				
			||||||
	Network string `json:"network" yaml:"network" ini:"network" toml:"network"` // 网络连接方式
 | 
						Network string `json:"network" yaml:"network" ini:"network" toml:"network"` // 网络连接方式
 | 
				
			||||||
@ -41,3 +43,36 @@ type Options struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	IdentitySuffix string `json:"identity_suffix" yaml:"identity_suffix" ini:"identity_suffix" toml:"identity_suffix"` // Add suffix to client name. Default is empty.
 | 
						IdentitySuffix string `json:"identity_suffix" yaml:"identity_suffix" ini:"identity_suffix" toml:"identity_suffix"` // Add suffix to client name. Default is empty.
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Config 数据库连接配置
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Author : go_developer@163.com<白茶清欢>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Date : 15:59 2024/10/8
 | 
				
			||||||
 | 
					type Config struct {
 | 
				
			||||||
 | 
						ReadOnly bool     `json:"readonly" yaml:"readonly" ini:"readonly" toml:"readonly"`
 | 
				
			||||||
 | 
						Master   *Options `json:"master" yaml:"master" ini:"master" toml:"master"` // 主库配置
 | 
				
			||||||
 | 
						Slave    *Options `json:"slave" yaml:"slave" ini:"slave" toml:"slave"`     // 主库配置
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ClientInfo 客户端连接信息
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Author : go_developer@163.com<白茶清欢>
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Date : 16:06 2024/10/8
 | 
				
			||||||
 | 
					type ClientInfo struct {
 | 
				
			||||||
 | 
						ReadOnly bool                // 是否只读
 | 
				
			||||||
 | 
						Master   *redisClient.Client // 主库连接
 | 
				
			||||||
 | 
						Slave    *redisClient.Client // 从库连接
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cf *ClientInfo) MasterClient() *redisClient.Client {
 | 
				
			||||||
 | 
						return cf.Master
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (cf *ClientInfo) SlaveClient() *redisClient.Client {
 | 
				
			||||||
 | 
						if nil != cf.Slave {
 | 
				
			||||||
 | 
							return cf.Slave
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cf.Master
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user