Замените Sleep на WaitGroup: Add вызывается до запуска, Done — через defer, Wait — после цикла. В каждом запросе обработайте сетевую ошибку, HTTP-статус и чтение тела.
Конкурентный загрузчик веб‑страниц
Замените Sleep на WaitGroup: Add вызывается до запуска, Done — через defer, Wait — после цикла. В каждом запросе обработайте сетевую ошибку, HTTP-статус и чтение тела.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Необходимо доработать программу, чтобы:
- избавиться от использования
time.Sleepдля ожидания завершения горутин; - корректно обрабатывать возникающие ошибки;
- дождаться окончания выполнения всех запросов.
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
urls := []string{
"https://www.lamoda.ru",
"https://www.yandex.ru",
"https://www.mail.ru",
"https://www.google.com",
}
for _, url := range urls {
go func(url string) {
fmt.Printf("Fetching %s...\n", url)
err := fetchUrl(url)
if err != nil {
fmt.Printf("Error fetching %s: %v\n", url, err)
return
}
fmt.Printf("Fetched %s\n", url)
}(url)
}
fmt.Println("All requests launched!")
time.Sleep(400 * time.Millisecond)
fmt.Println("Program finished.")
}
func fetchUrl(url string) error {
// Подробная реализация опущена и не относится к теме задачи
// _, err := http.Get(url)
return nil
}
Ответ
Замените Sleep на WaitGroup: Add вызывается до запуска, Done — через defer, Wait — после цикла. В каждом запросе обработайте сетевую ошибку, HTTP-статус и чтение тела.
Ниже демонстрационный список URL можно заменить списком из условия. Импорты: fmt, io, net/http, sync, time.
func main() {
urls := []string{"https://example.com", "https://example.org"}
client := &http.Client{Timeout: 5 * time.Second}
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go func(url string) {
defer wg.Done()
resp, err := client.Get(url)
if err != nil {
fmt.Printf("Error fetching %s: %v\n", url, err)
return
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("Error fetching %s: HTTP %d\n", url, resp.StatusCode)
return
}
_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
fmt.Printf("Error reading %s: %v\n", url, err)
return
}
fmt.Printf("Fetched %s\n", url)
}(url)
}
wg.Wait()
fmt.Println("Program finished.")
}
Успех HTTP не гарантируется отсутствием ошибки Get, поэтому статус проверяется отдельно. Тайм-аут ограничивает всё обращение. Для большого списка добавьте ограниченную конкурентность; для недоверенных ответов — лимит объёма тела. Здесь данные прочитываются и отбрасываются, файлы на диск не сохраняются.