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.
|
// nil означает режим "только память" — все функции тихо no-op.
|
||||||
var Client *redis.Client
|
var Client *redis.Client
|
||||||
|
|
||||||
|
// Параметры подключения — сохраняются для переподключения после обрыва.
|
||||||
|
var (
|
||||||
|
redisAddr string
|
||||||
|
redisUsername string
|
||||||
|
redisPassword string
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
seqMu sync.Mutex
|
seqMu sync.Mutex
|
||||||
queueWriteSeq = map[string]uint64{}
|
queueWriteSeq = map[string]uint64{}
|
||||||
@@ -53,6 +60,10 @@ func Connect(addr, username, password string) error {
|
|||||||
log.Info("persistence: REDIS_ADDR не задан, работаем в memory-only режиме")
|
log.Info("persistence: REDIS_ADDR не задан, работаем в memory-only режиме")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
redisAddr = addr
|
||||||
|
redisUsername = username
|
||||||
|
redisPassword = password
|
||||||
|
|
||||||
rdb := redis.NewClient(&redis.Options{
|
rdb := redis.NewClient(&redis.Options{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Username: username,
|
Username: username,
|
||||||
@@ -66,6 +77,55 @@ func Connect(addr, username, password string) error {
|
|||||||
}
|
}
|
||||||
Client = rdb
|
Client = rdb
|
||||||
log.Infof("persistence: подключились к Redis %s", addr)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user