You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
2.3 KiB
110 lines
2.3 KiB
package database |
|
|
|
import ( |
|
"context" |
|
"gofaster/internal/shared/config" |
|
"time" |
|
|
|
"github.com/go-redis/redis/v8" |
|
"go.uber.org/zap" |
|
) |
|
|
|
type RedisClient struct { |
|
client *redis.Client |
|
logger *zap.Logger |
|
} |
|
|
|
func NewRedisClient(cfg *config.RedisConfig, logger *zap.Logger) *RedisClient { |
|
if logger == nil { |
|
logger = zap.L() |
|
} |
|
|
|
// 记录连接参数(隐藏密码) |
|
logger.Info("Initializing Redis client", |
|
zap.String("host", cfg.Host), |
|
zap.String("port", cfg.Port), |
|
zap.Int("db", cfg.DB), |
|
zap.Bool("has_password", cfg.Password != ""), |
|
) |
|
|
|
client := redis.NewClient(&redis.Options{ |
|
Addr: cfg.Host + ":" + cfg.Port, |
|
Password: cfg.Password, |
|
DB: cfg.DB, |
|
}) |
|
|
|
// 测试连接(带超时和重试) |
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) |
|
defer cancel() |
|
|
|
retries := 3 |
|
var lastErr error |
|
for i := 0; i < retries; i++ { |
|
if _, err := client.Ping(ctx).Result(); err != nil { |
|
lastErr = err |
|
logger.Warn("Redis connection attempt failed", |
|
zap.Int("attempt", i+1), |
|
zap.Error(err), |
|
) |
|
time.Sleep(1 * time.Second) // 延迟重试 |
|
continue |
|
} |
|
break |
|
} |
|
|
|
if lastErr != nil { |
|
logger.Warn("Failed to connect to Redis after retries, continuing without Redis", |
|
zap.Int("retries", retries), |
|
zap.Error(lastErr), |
|
) |
|
// 返回一个空的 Redis 客户端,程序可以继续运行 |
|
return &RedisClient{ |
|
client: nil, |
|
logger: logger, |
|
} |
|
} |
|
|
|
// 监控连接状态 |
|
go monitorConnection(client, logger) |
|
|
|
logger.Info("Redis client initialized successfully") |
|
return &RedisClient{ |
|
client: client, |
|
logger: logger, |
|
} |
|
} |
|
|
|
// 监控 Redis 连接状态 |
|
func monitorConnection(client *redis.Client, logger *zap.Logger) { |
|
if client == nil { |
|
return // 如果没有 Redis 客户端,不进行监控 |
|
} |
|
|
|
ticker := time.NewTicker(30 * time.Second) |
|
defer ticker.Stop() |
|
|
|
for range ticker.C { |
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) |
|
_, err := client.Ping(ctx).Result() |
|
cancel() |
|
|
|
if err != nil { |
|
logger.Error("Redis connection health check failed", |
|
zap.Error(err), |
|
) |
|
} |
|
} |
|
} |
|
|
|
func (r *RedisClient) GetClient() *redis.Client { |
|
return r.client |
|
} |
|
|
|
func (r *RedisClient) Close() error { |
|
if r.client == nil { |
|
r.logger.Info("No Redis connection to close") |
|
return nil |
|
} |
|
r.logger.Info("Closing Redis connection") |
|
return r.client.Close() |
|
}
|
|
|