Comments 36
Я ни разу не программист на Go, но человеку, уверенно решающему задачу вычисления " сумму адресов" по списку URL я бы отказал. Потому что он не в состоянии объяснить что же именно он вычисляет. И второй пункт - опечатки в коде
Спасибо за комментарий. Можете подсказать, что именно про опечатки в коде вы имеете ввиду?
А "сумма тел" (видимо, имелось ввиду html <body>), а также "время выполнение" Вас не смутило?
Как по мне, это либо статья написанная целиком ГПТ или перевод откуда-то. Прошу прощения, если я ошибаюсь и для автора статьи русский не родной язык.
Как хорошо, что не программисты не нанимают программистов. По вашему комментарию я понял, что вы не понимаете сути программирования, а "смотрите на рюшечки".
Поначалу вообще не въехал в постановку задачи: как это " вычислить сумму тел ответов"?!? И почему автор решил что это про сумму времени получения ответов? А если в ответ по этим урл должны прилетать чиселки, что тогда?
Канал результатов в последнем примере (другие не смотрел) можно делать небуферизованным - это сэкономит немного памяти.
Для объединения ошибок есть errors.Join
Для вычисления "время с момента Х" есть time.Since
При заранее известном размере пула проще сразу запустить Х рутин, в которых читать канал с урлами, ибо запуск рутин тоже не бесплатная операция, а их тут по одной ка каждый урл
Уж оптимизировать, так оптимизировать) Чтобы "сэкономить немного памяти" можно обойтись без канала результатов, а защитить счетчик результатов sync/atomic https://pkg.go.dev/sync/atomic из стандартной библлиотеки. В частности https://pkg.go.dev/sync/atomic#Int64.Add
ещё есть MultiErrors для сбора ошибок
На собесе по этой задаче, скорее всего, хотели увидеть вот это https://pkg.go.dev/golang.org/x/sync/errgroup
golang.org/x -- придворное тюнинговое ателье, почти stdlib)
Там этот кейс первым примером. Вы расписали, что там под капотом, а можно просто использовать готовое решение.
ЧатГПТ же. Абсолютно бестолковая постановка задачи и не менее бестолковая оптимизация.
На собеседовании с таким вопросом я бы долго допытывался: А что задающему этот вопрос на самом деле надо? Ну глупо же просить написать GET в цикле, а потом распаралелить эти геты в пуле. Это не даст никаких знаний о кандидате. Наверно от меня хотят понимания миллиона корнер кейсов этой задачи. Вроде переадресаций, протоколов, проксей, кук, защиты от роботов, флапов сети и подобного. Это уже интересно и даст определенные знания о кандидате.
А если не пытаться вычитывать все тело ответа (а вдруг там много гигабайт?), то можно упростить код.
Вместо:
body, err := io.ReadAll(resp.Body)
if err != nil {
ansCh <- respStCWP{lenBody: 0, err: err}
return
}
ansCh <- respStCWP{lenBody: int64(len(body)), err: nil}
Написать:
lenBody, err := io.Copy(io.Discard, resp.Body)
ansCh <- respStCWP{lenBody: lenBbody, err: err}
if err != nil {
return
}
А проверяли кейс, когда сервер отдаст 200 и пустое тело ответа?
Если бы мне дали именно такую задачу, то я читал бы только заголовки и брал длину оттуда.
Зачем читать тело целиком!
тогда уж не GET, а HEAD надо делать
но сильно подозреваю, что если дадут файл с 1млн+ адресами ссылками, больше 25% либо не будут поддерживать HEAD, либо не будут отдавать длину ответа.
Я делал аналогичный проект (кстати тоже на go), там был список под 5млн адресов/серверов и запросы надо было сделать за ограниченное время. Увы, не все так хорошо с соблюдением HTTP стандартов как хотелось бы.
Это задача для собеседования и тут можно "хитрить". Зададут вопрос про url не соблюдающие HTTP, то предлагаете другой способ. Тут важно показать ход ваших мыслей. Что вы готовы решить как по-простому варианту (иногда бывает этого более чем достаточно) и готовы решить со всеми известными исключениями.
Получается вы можете из одной задачи "выжать" 2 варианта с полной выкладкой почему и как). С умом надо к собеседованию подходить!
Это вам видимо хорошие сайты подали на вход.
Во многих местах - его не заполняют.
Классический пример «скорость/качество/стоимость - выберите любые два». По хорошему, для максимально быстрого кода тут нужно делать запросы HEAD и анализировать ответы (сжаты ли данные, версия протокола и т.д.) и на основании этих данных по разному обрабатывать каждый урл (делать повторно GET и подсчитывать фактическое количество вернувшихся байт или довериться данным из заголовков).
Вот тут ошибка будет, получается запрос N раз одного и того же адреса (go <= 1.23)
for _, url := range urls {
semaphore <- struct{}{}
wg.Add(1)
go func(u string) {}(url)
}
Если же делать параллельную обработку в несколько потоков в пуле, то правильнее все-таки делать вот так
const poolSize = 3
type ResultData struct {
BodyLen int64
Err error
}
addressCh := make(chan string, 3)
resultCh := make(chan ResultData, 3)
wg := &sync.WaitGroup{}
wg.Add(poolSize)
for c := 0; c <= poolSize; c++ {
go func() {
for address := range addressCh {
body, err := doRequest(address)
resultCh <- ResultData{BodyLen: len(body), Err: err}
}
wg.Done()
}()
}
go func() {
urls := []string{"1","2","3"}
for _, url := range urls {
addressCh <- url
}
close(addressCh)
}()
type Result struct {
AllLen int64
Errors []error
}
result := Result{
AllLen: 0,
Errors: make([]error, 0),
}
go func() {
for reqRes := range resultCh {
if reqRes.Err != nil {
result.Errors = append(result.Errors, reqRes.Err)
} else {
result.AllLen += reqRes.BodyLen
}
}
}()
wg.Wait()
close(resultCh)
fmt.Println(result)</code></pre><p><br> Все остальное от лукавого</p>
Задача «Получить значение у N url из списка» с собеседования на Go