Я создаю бота Mattermost. Он перестает отвечать после того, как соединение через веб-сокет получает тайм-аут пинга (PingTimeoutChannel) после случайных периодов времени (1 минута, 8 минут, 2 часа и т. д.). Сервер Mattermost v.5.13, API v.4.
Бот подключается к Mattermost API, создавая новый Client4. Затем он входит в систему как пользователь и после этого создает клиент Websocket с полученным токеном авторизации. Он начинает слушать на всех каналах, и когда он получает событие, которое является сообщением, адресованным ему (@botname), он автоматически отвечает (создает model.post).
Я решил использовать простую аутентификацию по имени пользователя/паролю для входа в систему, как и в образце бота Mattermost. Однако я попытался переписать его на аутентификацию токена личного доступа (как в здесь), потому что я думал, что это решит проблему тайм-аута. Однако это решение больше не работает, оно выдает «Ошибка недействительного или просроченного сеанса, пожалуйста, войдите снова» при попытке войти в систему таким образом.
Поэтому я отказался от этой идеи и начал искать, где происходит тайм-аут. Пинг сервера в порядке, вебсокета нет. Я пробовал много способов, вплоть до того, что просто переподключился (снова создав новые клиенты Mattermost API и Websocket). Бот по-прежнему не отвечает. У меня закончились идеи.
Соединение через веб-сокет (обработка ошибок пропущена):
if config.BotCfg.Port == "443" {
protocol = "https"
secure = true
}
config.ConnectionCfg.Client = model.NewAPIv4Client(fmt.Sprintf("%s://%s:%s", protocol, config.BotCfg.Server, config.BotCfg.Port))
user,resp := config.ConnectionCfg.Client.Login(config.BotCfg.BotName, config.BotCfg.Password)
setBotTeam()
if limit.Users == nil {
limit.SetUsersList()
}
ws := "ws"
if secure {
ws = "wss"
}
if Websocket != nil {
Websocket.Close()
}
websocket, err := model.NewWebSocketClient4(fmt.Sprintf("%s://%s:%s", ws, config.BotCfg.Server, config.BotCfg.Port), config.ConnectionCfg.Client.AuthToken)
Функция прослушивания:
for {
select {
case <-connection.Websocket.PingTimeoutChannel:
logs.WriteToFile("Websocket ping timeout. Connecting again.")
log.Println("Websocket ping timeout. Connecting again.")
mux.Lock()
connection.Connect()
mux.Unlock()
case event := <-connection.Websocket.EventChannel:
mux.Lock()
if event != nil {
if event.IsValid() && isMessage(event.Event){
handleEvent(event)
}
}
mux.Unlock()
}
}
}()
// block to the go function
select {}
Я ожидаю, что бот будет работать непрерывно. Если у вас есть какие-либо предложения, как решить эту проблему, я был бы очень признателен!
Изменить: как предложил Cerise, я добавил SIGQUIT в функцию выхода и запустил детектор гонки. Исправлена проблема с гонкой данных, удалив один if из события case := [...]. Детектор гонки больше не сообщает о каких-либо проблемах, однако бот по-прежнему перестает отвечать через некоторое время.
Я обнаружил, что при первом возникновении PingTimeout одноранговый узел перестает отвечать, пока я не перезапущу приложение. Переподключение Websocket не помогает. Однако я на самом деле не знаю, как решить эту проблему, или решение вообще существует.