P2.8: Redis reconnect/health-check
Connect сохраняет параметры подключения и запускает фоновую проверку (ping каждые 30с). При обрыве Redis-клиент оставался не-nil и записи молча падали в лог; теперь соединение восстанавливается автоматически через reconnect().
This commit is contained in:
@@ -31,6 +31,13 @@ import (
|
||||
// nil означает режим "только память" — все функции тихо no-op.
|
||||
var Client *redis.Client
|
||||
|
||||
// Параметры подключения — сохраняются для переподключения после обрыва.
|
||||
var (
|
||||
redisAddr string
|
||||
redisUsername string
|
||||
redisPassword string
|
||||
)
|
||||
|
||||
var (
|
||||
seqMu sync.Mutex
|
||||
queueWriteSeq = map[string]uint64{}
|
||||
@@ -53,6 +60,10 @@ func Connect(addr, username, password string) error {
|
||||
log.Info("persistence: REDIS_ADDR не задан, работаем в memory-only режиме")
|
||||
return nil
|
||||
}
|
||||
redisAddr = addr
|
||||
redisUsername = username
|
||||
redisPassword = password
|
||||
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: addr,
|
||||
Username: username,
|
||||
@@ -66,6 +77,55 @@ func Connect(addr, username, password string) error {
|
||||
}
|
||||
Client = rdb
|
||||
log.Infof("persistence: подключились к Redis %s", addr)
|
||||
startRedisHealthCheck()
|
||||
return nil
|
||||
}
|
||||
|
||||
// startRedisHealthCheck — фоновая проверка соединения и переподключение.
|
||||
// После обрыва Redis-клиент остаётся не-nil, но записи молча падают. Здесь мы
|
||||
// периодически пингуем Redis и при сбое создаём новый клиент (reconnect), чтобы
|
||||
// персистентность восстановилась автоматически.
|
||||
func startRedisHealthCheck() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
if Client == nil {
|
||||
continue
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
err := Client.Ping(ctx).Err()
|
||||
cancel()
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
log.Warnf("persistence: Redis недоступен (%v), пробуем переподключиться", err)
|
||||
if reErr := reconnect(); reErr != nil {
|
||||
log.Errorf("persistence: переподключение не удалось: %v", reErr)
|
||||
} else {
|
||||
log.Infof("persistence: переподключились к Redis %s", redisAddr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// reconnect — создаёт новый Redis-клиент с сохранёнными параметрами подключения.
|
||||
func reconnect() error {
|
||||
if redisAddr == "" {
|
||||
return fmt.Errorf("redis addr пуст")
|
||||
}
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: redisAddr,
|
||||
Username: redisUsername,
|
||||
Password: redisPassword,
|
||||
DB: 0,
|
||||
})
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := rdb.Ping(ctx).Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
Client = rdb
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user