Golang периодический опрос серверов перестаёт работать
Программа пашет на протяжении нескольких минут, ломается и выдаёт ошибку. Код ошибки:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x50 pc=0x5d8063]
goroutine 35 [running]:
main.getStatistics({0xc000190040, 0x9})
C:/Program Files/xampp/server/server.go:71 +0xc3
created by main.main
C:/Program Files/xampp/server/server.go:62 +0x3b9
Логика программы: 1) Получаем ip адреса всех устройств из mysql 2) В бесконечном цикле вызываем для каждого ip функцию и запускаем ее в горутине 3) В функции отсылаем по tcp всем устройствам набор байт и слушаем ответ 4) Из полученных данных собираем ответ и пишем в redis
func main() {
db, err := sql.Open("mysql", "test:@/test")
if err != nil {
panic(err)
}
rows, err := db.Query("select * from inclunometers")
if err != nil {
panic(err)
}
inclunometers := []inclunometer{}
for rows.Next() {
p := inclunometer{}
err := rows.Scan(&p.id, &p.Type, &p.accuracy, &p.ip, &p.rs485_address, &p.side_id)
if err != nil {
fmt.Println(err)
continue
}
inclunometers = append(inclunometers, p)
}
defer rows.Close()
defer db.Close()
for _, element := range inclunometers {
for {
go getStatistics(element.ip)
time.Sleep(500 * time.Millisecond)
}
}
}
Ранее вызывал эту функцию раз в 8 секунд и всё стабильно работало на протяжении как минимум нескольких дней, но появилась необходимость вызывать её два раза в секунду.
func getStatistics(ip string) {
conn, _ := net.Dial("tcp", string(ip+":50"))
_, err := conn.Write([]byte{0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xC4, 0x0B})
if err != nil {
panic(err)
}
windSpeedStatistics := make([]byte, 10)
message, _ := bufio.NewReader(conn).Read(windSpeedStatistics)
if message == 0 {
panic("can`t get wind stats")
}
windSpeed := (float64(binary.BigEndian.Uint16(windSpeedStatistics[3:5]) / 100))
_, err = conn.Write([]byte{0x01, 0x03, 0x00, 0x01, 0x00, 0x02, 0x95, 0xCB})
if err != nil {
panic(err)
}
windDirection := make([]byte, 10)
message, _ = bufio.NewReader(conn).Read(windDirection)
if message == 0 {
panic("can`t get wind stats")
}
direction := (float64(binary.BigEndian.Uint16(windDirection[3:5])) / 100)
prepareJson := jsonStatistics{Date.Format("2006-01-02 15:04:05"), ip, direction, windSpeed}
jsonDecode, err := json.Marshal(prepareJson)
if err != nil {
panic(err)
}
/*REDIS*/
redisKey := "ip:" + ip
currUnix := time.Now().Unix()
redisConn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
v, err := redisConn.Do("ZADD", redisKey, int(currUnix), jsonDecode)
if v == 0 || err != nil {
panic(err)
}
/*REDIS*/
defer conn.Close()
defer redisConn.Close()
return windSpeed, direction
}
Ответы (1 шт):
Автор решения: young_protokaa42
→ Ссылка
Проблема была решена внесением ряда изменений:
1) Ранее, в функции main для каждого устройства запускался бесконечный цикл.
Теперь же в горутине вызывается отдельная функция.
2) Выяснилось, что функция не успевает отрабатывать два раза в секунду.
Был убран её периодический опрос. Теперь она снова запустится,
как только выполнится, отправив в callSurvey результат true.
3) В прошлой версии соединение tcp открывалось и закрывалось при каждом прохождении
функции опроса. Теперь соединение открывается в начале программы и так и висит открытым.
Не знаю, насколько плохо держать соединение длительное время открытым,
но программа показывает хороший результат, бесперебойно работая уже как четверо суток.
for _, element := range inclunometers {
go callSurvey(element.ip)
}
func callSurvey(ip string) {
conn, err := net.Dial("tcp", string(ip+":50"))
if err != nil {
fmt.Println(time.Now(), err)
}
for {
result := getStatistics(ip, conn)
DELAY:
time.Sleep(100 * time.Millisecond)
if !result {
goto DELAY
}
}
}
func getStatistics(ip string, conn net.Conn) bool {
...
return true;
}