// common/redis/redis.go package redis import ( "context" "errors" "fmt" "sync" "time" "github.com/redis/go-redis/v9" ) // 全局单例Redis客户端(连接池) var ( clientInstance *redis.Client once sync.Once initErr error ) // RedisOptions Redis连接配置项 // 包含基础连接信息+连接池配置,按需配置 type RedisOptions struct { Host string // Redis地址 Port string // Redis端口 Password string // Redis密码 DB int // 数据库索引(建议每个业务一个DB,如user=0, order=1) PoolSize int // 连接池最大连接数 MinIdleConns int // 连接池最小空闲连接数 ConnTimeout time.Duration // 连接超时时间 ReadTimeout time.Duration // 读超时时间 WriteTimeout time.Duration // 写超时时间 IdleTimeout time.Duration // 连接空闲超时时间 } // RedisOption 选项模式函数类型 type RedisOption func(*RedisOptions) // defaultRedisOptions 初始化默认配置 // 开发环境直接对接根目录docker-compose的redis服务,无需额外配置 func defaultRedisOptions() *RedisOptions { return &RedisOptions{ Host: "redis", // 匹配Compose服务名 Port: "6379", // 默认端口 Password: "", // 开发环境默认无密码 DB: 0, // 默认DB0 PoolSize: 50, // 连接池最大连接数 MinIdleConns: 10, // 最小空闲连接,避免频繁创建连接 ConnTimeout: 5 * time.Second, ReadTimeout: 3 * time.Second, WriteTimeout: 3 * time.Second, IdleTimeout: 30 * time.Minute, } } // 以下为配置项设置函数,支持链式调用 func WithHost(host string) RedisOption { return func(o *RedisOptions) { o.Host = host } } func WithPort(port string) RedisOption { return func(o *RedisOptions) { o.Port = port } } func WithPassword(pwd string) RedisOption { return func(o *RedisOptions) { o.Password = pwd } } func WithDB(db int) RedisOption { return func(o *RedisOptions) { o.DB = db } } func WithPoolSize(size int) RedisOption { return func(o *RedisOptions) { o.PoolSize = size } } func WithLogLevel(level redis.LogLevel) RedisOption { return func(o *RedisOptions) { // 兼容日志级别配置,需配合客户端初始化 } } // InitRedis 初始化Redis单例客户端(连接池) // 选项模式配置,仅执行一次,全局复用连接池 func InitRedis(opts ...RedisOption) (*redis.Client, error) { once.Do(func() { // 加载默认配置 + 覆盖用户自定义配置 options := defaultRedisOptions() for _, opt := range opts { opt(options) } // 构建Redis客户端配置 rdbOpts := &redis.Options{ Addr: fmt.Sprintf("%s:%s", options.Host, options.Port), Password: options.Password, DB: options.DB, PoolSize: options.PoolSize, MinIdleConns: options.MinIdleConns, DialTimeout: options.ConnTimeout, ReadTimeout: options.ReadTimeout, WriteTimeout: options.WriteTimeout, IdleTimeout: options.IdleTimeout, } // 创建客户端实例 clientInstance = redis.NewClient(rdbOpts) // 测试连接,确保连接有效 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if _, err := clientInstance.Ping(ctx).Result(); err != nil { initErr = fmt.Errorf("redis ping failed: %w", err) clientInstance = nil // 连接失败置空实例 return } initErr = nil }) return clientInstance, initErr } // GetRedis 获取全局Redis单例客户端 // 业务层初始化后,直接调用获取,无需重复初始化 func GetRedis() (*redis.Client, error) { if clientInstance == nil { return nil, errors.New("redis not initialized, please call InitRedis first") } return clientInstance, nil } // CloseRedis 关闭Redis连接池(应用退出时调用,优雅释放资源) func CloseRedis() error { if clientInstance == nil { return errors.New("redis not initialized") } if err := clientInstance.Close(); err != nil { return fmt.Errorf("close redis failed: %w", err) } // 重置单例,避免重复关闭 once = sync.Once{} clientInstance = nil initErr = errors.New("redis client closed") return nil } // --------------- 基础操作封装 --------------- // 轻量封装高频基础操作,简化业务层调用(无需关注context和错误处理基础逻辑) // 复杂操作(如哈希、列表、事务)直接使用原生*redis.Client即可 // Ctx 全局默认context,业务层可自定义传入 var Ctx = context.Background() // Set 封装Set操作,带过期时间 // key: 键, val: 值, expire: 过期时间(0表示永不过期) func Set(key string, val interface{}, expire time.Duration) error { rdb, err := GetRedis() if err != nil { return err } return rdb.Set(Ctx, key, val, expire).Err() } // Get 封装Get操作,返回字符串值 func Get(key string) (string, error) { rdb, err := GetRedis() if err != nil { return "", err } return rdb.Get(Ctx, key).Result() } // Del 封装Del操作,删除一个或多个键 func Del(keys ...string) (int64, error) { rdb, err := GetRedis() if err != nil { return 0, err } return rdb.Del(Ctx, keys...).Result() } // Exists 检查键是否存在 func Exists(key string) (bool, error) { rdb, err := GetRedis() if err != nil { return false, err } res, err := rdb.Exists(Ctx, key).Result() return res > 0, err } // Expire 为键设置过期时间 func Expire(key string, expire time.Duration) error { rdb, err := GetRedis() if err != nil { return err } return rdb.Expire(Ctx, key, expire).Err() } // HSet 封装哈希Set操作,设置单个字段 func HSet(key, field string, val interface{}) error { rdb, err := GetRedis() if err != nil { return err } return rdb.HSet(Ctx, key, field, val).Err() } // HGet 封装哈希Get操作,获取单个字段值 func HGet(key, field string) (string, error) { rdb, err := GetRedis() if err != nil { return "", err } return rdb.HGet(Ctx, key, field).Result() }