Files
trading_assistant/trading_assistant_api/common/redis/redis.go
2026-02-06 16:48:36 +08:00

220 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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()
}