140 lines
3.8 KiB
Go
140 lines
3.8 KiB
Go
package utils
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/goravel/framework/facades"
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
var (
|
|
// redisClients 缓存不同连接名的 Redis 客户端
|
|
redisClients sync.Map
|
|
// redisMutex 用于保护客户端创建过程
|
|
redisMutex sync.Mutex
|
|
)
|
|
|
|
// GetRedisClient 获取 Redis 客户端(使用连接池,支持多连接)
|
|
// connectionName: Redis 连接名称,默认为 "default"
|
|
// 返回缓存的 Redis 客户端,如果不存在则创建并缓存
|
|
func GetRedisClient(connectionName string) (*redis.Client, error) {
|
|
if connectionName == "" {
|
|
connectionName = "default"
|
|
}
|
|
|
|
// 先从缓存中获取
|
|
if client, ok := redisClients.Load(connectionName); ok {
|
|
redisClient := client.(*redis.Client)
|
|
// 测试连接是否有效
|
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
|
err := redisClient.Ping(ctx).Err()
|
|
cancel()
|
|
if err == nil {
|
|
return redisClient, nil
|
|
}
|
|
// 连接失效,从缓存中移除
|
|
redisClients.Delete(connectionName)
|
|
}
|
|
|
|
// 使用互斥锁确保只创建一个客户端
|
|
redisMutex.Lock()
|
|
defer redisMutex.Unlock()
|
|
|
|
// 双重检查,防止并发创建
|
|
if client, ok := redisClients.Load(connectionName); ok {
|
|
return client.(*redis.Client), nil
|
|
}
|
|
|
|
// 创建新的 Redis 客户端
|
|
client, err := createRedisClient(connectionName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 缓存客户端
|
|
redisClients.Store(connectionName, client)
|
|
|
|
return client, nil
|
|
}
|
|
|
|
// createRedisClient 创建 Redis 客户端
|
|
func createRedisClient(connectionName string) (*redis.Client, error) {
|
|
// 获取 Redis 配置
|
|
host := facades.Config().GetString(fmt.Sprintf("database.redis.%s.host", connectionName), "")
|
|
if host == "" {
|
|
// 尝试使用 default 连接
|
|
host = facades.Config().GetString("database.redis.default.host", "127.0.0.1")
|
|
}
|
|
|
|
port := facades.Config().GetInt(fmt.Sprintf("database.redis.%s.port", connectionName), 0)
|
|
if port == 0 {
|
|
port = facades.Config().GetInt("database.redis.default.port", 6379)
|
|
}
|
|
|
|
password := facades.Config().GetString(fmt.Sprintf("database.redis.%s.password", connectionName), "")
|
|
if password == "" {
|
|
password = facades.Config().GetString("database.redis.default.password", "")
|
|
}
|
|
|
|
db := facades.Config().GetInt(fmt.Sprintf("database.redis.%s.database", connectionName), -1)
|
|
if db == -1 {
|
|
db = facades.Config().GetInt("database.redis.default.database", 0)
|
|
}
|
|
|
|
// 创建 Redis 客户端(使用连接池配置)
|
|
client := redis.NewClient(&redis.Options{
|
|
Addr: fmt.Sprintf("%s:%d", host, port),
|
|
Password: password,
|
|
DB: db,
|
|
PoolSize: 10, // 连接池大小
|
|
MinIdleConns: 5, // 最小空闲连接数
|
|
MaxRetries: 3, // 最大重试次数
|
|
})
|
|
|
|
// 测试连接(设置超时)
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
|
|
_, err := client.Ping(ctx).Result()
|
|
if err != nil {
|
|
client.Close() // 连接失败,关闭客户端
|
|
return nil, fmt.Errorf("Redis 连接失败 [%s]: %v", connectionName, err)
|
|
}
|
|
|
|
return client, nil
|
|
}
|
|
|
|
// CloseRedisClient 关闭指定的 Redis 客户端并从缓存中移除
|
|
func CloseRedisClient(connectionName string) error {
|
|
if connectionName == "" {
|
|
connectionName = "default"
|
|
}
|
|
|
|
if client, ok := redisClients.LoadAndDelete(connectionName); ok {
|
|
redisClient := client.(*redis.Client)
|
|
return redisClient.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CloseAllRedisClients 关闭所有缓存的 Redis 客户端
|
|
func CloseAllRedisClients() error {
|
|
var errs []error
|
|
redisClients.Range(func(key, value any) bool {
|
|
client := value.(*redis.Client)
|
|
if err := client.Close(); err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
redisClients.Delete(key)
|
|
return true
|
|
})
|
|
|
|
if len(errs) > 0 {
|
|
return fmt.Errorf("关闭 Redis 客户端时发生错误: %v", errs)
|
|
}
|
|
return nil
|
|
}
|