package redis import ( "carrotskin/pkg/config" "fmt" "os" "sync" "github.com/alicebob/miniredis/v2" redis9 "github.com/redis/go-redis/v9" "go.uber.org/zap" ) var ( // clientInstance 全局Redis客户端实例 clientInstance *Client // once 确保只初始化一次 once sync.Once // initError 初始化错误 initError error // miniredisInstance 用于测试/开发环境 miniredisInstance *miniredis.Miniredis ) // Init 初始化Redis客户端(线程安全,只会执行一次) // 如果Redis连接失败且环境为测试/开发,则回退到miniredis func Init(cfg config.RedisConfig, logger *zap.Logger) error { var err error once.Do(func() { // 尝试连接真实Redis clientInstance, err = New(cfg, logger) if err != nil { logger.Warn("Redis连接失败,尝试使用miniredis回退", zap.Error(err)) // 检查是否允许回退到miniredis(仅开发/测试环境) if allowFallbackToMiniRedis() { clientInstance, err = initMiniRedis(logger) if err != nil { initError = fmt.Errorf("Redis和miniredis都初始化失败: %w", err) logger.Error("miniredis初始化失败", zap.Error(initError)) return } logger.Info("已回退到miniredis用于开发/测试环境") } else { initError = fmt.Errorf("Redis连接失败且不允许回退: %w", err) logger.Error("Redis连接失败", zap.Error(initError)) return } } }) return initError } // allowFallbackToMiniRedis 检查是否允许回退到miniredis func allowFallbackToMiniRedis() bool { // 检查环境变量 env := os.Getenv("ENVIRONMENT") return env == "development" || env == "test" || env == "dev" || os.Getenv("USE_MINIREDIS") == "true" } // initMiniRedis 初始化miniredis(用于开发/测试环境) func initMiniRedis(logger *zap.Logger) (*Client, error) { var err error miniredisInstance, err = miniredis.Run() if err != nil { return nil, fmt.Errorf("启动miniredis失败: %w", err) } // 创建Redis客户端连接到miniredis redisClient := redis9.NewClient(&redis9.Options{ Addr: miniredisInstance.Addr(), }) client := &Client{ Client: redisClient, logger: logger, } logger.Info("miniredis已启动", zap.String("addr", miniredisInstance.Addr())) return client, nil } // GetClient 获取Redis客户端实例(线程安全) func GetClient() (*Client, error) { if clientInstance == nil { return nil, fmt.Errorf("Redis客户端未初始化,请先调用 redis.Init()") } return clientInstance, nil } // MustGetClient 获取Redis客户端实例,如果未初始化则panic func MustGetClient() *Client { client, err := GetClient() if err != nil { panic(err) } return client } // Close 关闭Redis连接(包括miniredis如果使用了) func Close() error { var err error if miniredisInstance != nil { miniredisInstance.Close() miniredisInstance = nil } if clientInstance != nil { err = clientInstance.Close() clientInstance = nil } return err } // IsUsingMiniRedis 检查是否使用了miniredis func IsUsingMiniRedis() bool { return miniredisInstance != nil }