Principal
// Atnaujindami užsakymą, turime siųsti užsakymo duomenis keliems paslaugoms (trečiosioms šalims) // paslaugų skaičius auga (gali būti tūkstančiai+) // parašėme kodą, pradžioje viskas veikė gerai, bet laikui bėgant mūsų paslauga pradėjo naudoti daug išteklių // Reikia optimizuoti šį kodą, kad jis veiktų efektyviai su dideliu paslaugų skaičiumi func (s *orderService) SendOrder(ctx context.Context, hosts []string, order Order) { for i := 0; i < len(hosts); i++ { go func() { // Įsivaizduokite, kad tai yra ilgas tinklo iškvietimas response, err := s.httpClient.Send(ctx, hosts[i], order) if err != nil { s.logger.Error(ctx, "nepavyko išsiųsti", err) return } s.logger.Info(ctx, "sėkmė", response) }() } }
sobes.tech AI
Atsakymas iš AI
В представленном коде есть несколько проблем, которые приводят к высокому потреблению ресурсов при большом количестве сервисов:
-
Потенциальная утечка переменной цикла: в анонимной функции используется
hosts[i], ноiможет измениться к моменту выполнения горутины. Нужно передаватьhosts[i]как параметр функции. -
Запуск слишком большого количества горутин одновременно: если
hostsсодержит тысячи элементов, будет создано столько же горутин, что приведёт к высокому потреблению памяти и CPU. -
Отсутствие контроля параллелизма и ожидания завершения.
Как оптимизировать:
- Использовать пул горутин с ограничением по количеству одновременно выполняемых задач (например, через семафор или канал).
- Передавать значение
hostв горутину явно, чтобы избежать захвата переменной цикла. - Добавить механизм ожидания завершения всех отправок (например,
sync.WaitGroup).
Пример оптимизированного кода:
func (s *orderService) SendOrder(ctx context.Context, hosts []string, order Order) {
const maxConcurrency = 100 // ограничение по числу параллельных запросов
sem := make(chan struct{}, maxConcurrency)
var wg sync.WaitGroup
for _, host := range hosts {
wg.Add(1)
sem <- struct{}{} // захватываем слот
go func(h string) {
defer wg.Done()
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)
}
wg.Wait() // ждем завершения всех горутин
}
Такой подход позволяет контролировать нагрузку на систему, не создавая слишком много горутин одновременно, и предотвращает утечку переменной цикла.