This commit is contained in:
vipg
2026-02-06 16:48:36 +08:00
parent 3b65e135c2
commit 09192205bd
3 changed files with 612 additions and 0 deletions

View File

@@ -0,0 +1,220 @@
// 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()
}