Как избежать двойного соединения при восстановлении соединения?
В этих условиях:
- Клиент теряет сетевое подключение к zk.
- Проходит минута.
- Клиент восстанавливает сетевое подключение к zk.
Я получаю следующую панику:
panic: close of closed channel
goroutine 2849 [running]:
github.com/samuel/go-zookeeper/zk.(*Conn).Close(0xc420795180)
github.com/samuel/go-zookeeper/zk/conn.go:253 47
github.com/curator-go/curator.(*handleHolder).internalClose(0xc4203058f0, 0xc420302470, 0x0)
github.com/curator-go/curator/state.go:136 +0x8d
github.com/curator-go/curator.(*handleHolder).closeAndReset(0xc4203058f0, 0xc42587cd00, 0x1e)
github.com/curator-go/curator/state.go:122 +0x2f
github.com/curator-go/curator.(*connectionState).reset(0xc420302420, 0x1b71d87, 0xf)
github.com/curator-go/curator/state.go:234 +0x55
github.com/curator-go/curator.(*connectionState).handleExpiredSession(0xc420302420)
github.com/curator-go/curator/state.go:351 +0xd9
github.com/curator-go/curator.(*connectionState).checkState(0xc420302420, 0xffffff90, 0x0, 0x0, 0xc425ed2600, 0xed0e5250a)
github.com/curator-go/curator/state.go:318 +0x9c
github.com/curator-go/curator.(*connectionState).process(0xc420302420, 0xc425ed2680)
github.com/curator-go/curator/state.go:299 +0x16d
created by github.com/curator-go/curator.(*Watchers).Fire
github.com/curator-go/curator/watcher.go:64 +0x96
Это подробная последовательность событий:
- Клиент теряет сетевое подключение к zk.
- Проходит минута.
- Клиент восстанавливает сетевое подключение к zk.
- goroutine A называет
s.ReregisterAll()
→ Conn()
→ checkTimeout()
→ reset
(bc 1 минута истекло) → closeAndReset()
→ conn.Close()
, который может блокировать секунду
- goroutine B обрабатывает
zk.StateExpired
(кластер zk отправляет этот bc, он считает этот клиент мертвым, так как он не выполнял ping во время 2.) → reset
→ closeAndReset()
→ conn.Close()
, что вызывает панику потому что conn.Close()
уже закрыл канал соединения c.shouldQuit
. И s.zooKeeper.getZookeeperConnection
никогда не вызывался goroutine A, потому что он блокировал второй, поэтому нет нового соединения.
Наивное решение, которое я пробовал, - просто использовать мьютексы на reset
, но теперь я получаю helper.GetConnectionString()
равным пустой строке. Какой лучший способ избежать этого сбоя и попытаться войти в хорошее состояние, когда клиент потеряет, а затем восстановит сетевое подключение? Должно ли исправление быть в github.com/samuel/go-zookeeper, не позволяя закрыть уже закрытое соединение?
(Я написал этот вопрос здесь, но, похоже, проект не хватает в плане обсуждения, поэтому я спрашиваю о SO. )
Ответы
Ответ 1
zk.Conn имеет метод State(), который возвращает перечисление "Состояние", которое является одним из следующих:
type State int32
const (
StateUnknown State = -1
StateDisconnected State = 0
StateConnecting State = 1
StateAuthFailed State = 4
StateConnectedReadOnly State = 5
StateSaslAuthenticated State = 6
StateExpired State = -112
StateConnected = State(100)
StateHasSession = State(101)
)
Какое состояние "подключено", когда goroutine B вызывает conn.Close()?
Возможным решением было бы добавить переключатель в goroutine B, в котором вы не вызываете conn.Close(), если вы находитесь в conn.StateConnecting.