add
This commit is contained in:
@@ -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()
|
||||
}
|
||||
Reference in New Issue
Block a user