// A rendelés frissítésekor, a rendelés adatait több szolgáltatásnak (harmadik fél) kell küldeni // a szolgáltatások száma növekszik (lehet több ezer+) // megírtuk a kódot, kezdetben minden jól ment, de idővel a szolgáltatásunk sok erőforrást kezdett el fogyasztani func (s *orderService) SendOrder(ctx context.Context, hosts []string, order Order) { for i := 0; i < len(hosts); i++ { go func() { // Tegyük fel, hogy ez egy hosszú hálózati hívás response, err := s.httpClient.Send(ctx, hosts[i], order) if err != nil { s.logger.Error(ctx, "hiba a küldésben", err) return } s.logger.Info(ctx, "siker", response) }() } }
sobes.tech MI
Válasz az MI-től
В представленном коде есть проблема с использованием переменной цикла i внутри горутины:
for i := 0; i < len(hosts); i++ {
go func() {
response, err := s.httpClient.Send(ctx, hosts[i], order)
// ...
}()
}
Здесь i используется внутри анонимной функции, но к моменту запуска горутины значение i может измениться, что приведёт к ошибкам (например, выход за пределы массива).
Кроме того, при большом количестве сервисов (тысячи и более) запуск тысячи горутин одновременно может привести к чрезмерному потреблению ресурсов (память, CPU, сетевые соединения).
Рекомендации:
- Исправить замыкание переменной
i:
for i := 0; i < len(hosts); i++ {
host := hosts[i] // локальная копия
go func(h string) {
response, err := s.httpClient.Send(ctx, h, order)
// ...
}(host)
}
- Ограничить количество одновременно работающих горутин:
Использовать семафор или пул воркеров, чтобы не создавать слишком много горутин одновременно.
Пример с семафором:
sem := make(chan struct{}, 100) // максимум 100 одновременных горутин
for _, host := range hosts {
sem <- struct{}{} // захват семафора
go func(h string) {
defer func() { <-sem }() // освобождение семафора
response, err := s.httpClient.Send(ctx, h, order)
if err != nil {
s.logger.Error(ctx, "failed to send", err)
return
}
s.logger.Info(ctx, "success", response)
}(host)
}
// дождаться завершения всех горутин
for i := 0; i < cap(sem); i++ {
sem <- struct{}{}
}
- Использовать worker pool:
Создать фиксированное количество воркеров, которые будут читать из канала с хостами и отправлять запросы.
- Обрабатывать ошибки и таймауты:
Для устойчивости важно контролировать таймауты и корректно обрабатывать ошибки.
Таким образом, чтобы избежать роста потребления ресурсов, нужно контролировать параллелизм и правильно работать с переменными в замыканиях.