package cache import ( "context" "errors" "fmt" "strings" "giter.top/smart/pkg/config" "github.com/redis/go-redis/v9" ) // NewRedis 根据配置创建 Redis 客户端,支持单机、哨兵、集群三种模式。 func NewRedis(cfg *config.Config) (redis.UniversalClient) { if cfg == nil { panic("cache: config is nil") } r := cfg.Data.Redis mode := strings.ToLower(strings.TrimSpace(r.Mode)) if mode == "" { mode = "standalone" } switch mode { case "standalone": addr, err := standaloneAddr(cfg) if err != nil { panic(err) } opt := &redis.Options{ Addr: addr, DB: r.DB, } applyCommonToClient(opt, cfg) return redis.NewClient(opt) case "sentinel": if strings.TrimSpace(r.MasterName) == "" { panic("cache: redis sentinel requires master_name") } if len(r.Addrs) == 0 { panic("cache: redis sentinel requires addrs (sentinel 节点列表)") } opt := &redis.FailoverOptions{ MasterName: r.MasterName, SentinelAddrs: r.Addrs, DB: r.DB, } applyCommonToFailover(opt, cfg) return redis.NewFailoverClient(opt) case "cluster": if len(r.Addrs) == 0 { panic("cache: redis cluster requires addrs") } opt := &redis.ClusterOptions{ Addrs: r.Addrs, } applyCommonToCluster(opt, cfg) return redis.NewClusterClient(opt) default: panic(fmt.Sprintf("cache: unsupported redis mode %q", r.Mode)) } } func standaloneAddr(cfg *config.Config) (string, error) { r := cfg.Data.Redis if strings.TrimSpace(r.Addr) != "" { return r.Addr, nil } if len(r.Addrs) > 0 && strings.TrimSpace(r.Addrs[0]) != "" { return r.Addrs[0], nil } return "", errors.New("cache: redis standalone requires addr or addrs[0]") } // Ping 用于启动时探测连接是否可用。 func Ping(ctx context.Context, c redis.UniversalClient) error { if c == nil { return errors.New("cache: redis client is nil") } return c.Ping(ctx).Err() } func applyCommonToClient(opt *redis.Options, cfg *config.Config) { r := cfg.Data.Redis opt.Username = r.Username opt.Password = r.Password if r.PoolSize > 0 { opt.PoolSize = r.PoolSize } if r.MinIdleConns > 0 { opt.MinIdleConns = r.MinIdleConns } if r.MaxRetries != 0 { opt.MaxRetries = r.MaxRetries } if r.RetryDelay > 0 { opt.MinRetryBackoff = r.RetryDelay } if r.RetryMaxDelay > 0 { opt.MaxRetryBackoff = r.RetryMaxDelay } if r.DialTimeout > 0 { opt.DialTimeout = r.DialTimeout } if r.ReadTimeout > 0 { opt.ReadTimeout = r.ReadTimeout } if r.WriteTimeout > 0 { opt.WriteTimeout = r.WriteTimeout } if r.IdleTimeout > 0 { opt.ConnMaxIdleTime = r.IdleTimeout } } func applyCommonToFailover(opt *redis.FailoverOptions, cfg *config.Config) { r := cfg.Data.Redis opt.Username = r.Username opt.Password = r.Password if r.PoolSize > 0 { opt.PoolSize = r.PoolSize } if r.MinIdleConns > 0 { opt.MinIdleConns = r.MinIdleConns } if r.MaxRetries != 0 { opt.MaxRetries = r.MaxRetries } if r.RetryDelay > 0 { opt.MinRetryBackoff = r.RetryDelay } if r.RetryMaxDelay > 0 { opt.MaxRetryBackoff = r.RetryMaxDelay } if r.DialTimeout > 0 { opt.DialTimeout = r.DialTimeout } if r.ReadTimeout > 0 { opt.ReadTimeout = r.ReadTimeout } if r.WriteTimeout > 0 { opt.WriteTimeout = r.WriteTimeout } if r.IdleTimeout > 0 { opt.ConnMaxIdleTime = r.IdleTimeout } } func applyCommonToCluster(opt *redis.ClusterOptions, cfg *config.Config) { r := cfg.Data.Redis opt.Username = r.Username opt.Password = r.Password if r.PoolSize > 0 { opt.PoolSize = r.PoolSize } if r.MinIdleConns > 0 { opt.MinIdleConns = r.MinIdleConns } if r.MaxRetries != 0 { opt.MaxRetries = r.MaxRetries } if r.RetryDelay > 0 { opt.MinRetryBackoff = r.RetryDelay } if r.RetryMaxDelay > 0 { opt.MaxRetryBackoff = r.RetryMaxDelay } if r.DialTimeout > 0 { opt.DialTimeout = r.DialTimeout } if r.ReadTimeout > 0 { opt.ReadTimeout = r.ReadTimeout } if r.WriteTimeout > 0 { opt.WriteTimeout = r.WriteTimeout } if r.IdleTimeout > 0 { opt.ConnMaxIdleTime = r.IdleTimeout } }