Есть список ссылок. Нужно сходить по каждой GET-запросом и собрать коды
ответов в порядке исходного списка. Запросы идут параллельно, но
одновременно — не больше k, чтобы не положить ни себя, ни чужой сервис.
type Result struct {
StatusCode int // код ответа, если запрос удался
Err error // ошибка запроса; тогда StatusCode == 0
}
// FetchAll возвращает len(urls) результатов: Result[i] относится к urls[i].
// k <= 0 означает «без ограничения».
func FetchAll(ctx context.Context, urls []string, k int) []Result
Требования
len(result) == len(urls), result[i] — ответ на urls[i], независимо от
того, какой запрос закончился раньше.
- Одновременно выполняется не больше
k запросов, и при достаточном числе
ссылок лимит реально достигается (а не «по одному»). При k <= 0
все запросы идут параллельно.
- Ошибка одной ссылки (кривой URL, отказ соединения) попадает в
result[i].Err и не мешает остальным. Код ответа 4xx/5xx — это не ошибка,
а StatusCode.
- Запросы делаются с
ctx (через http.DefaultClient). После отмены ctx
новые запросы не начинаются, а FetchAll быстро возвращается; у
незавершённых ссылок Err != nil.
- Тело каждого ответа закрыто. После возврата горутин не остаётся.
Код проходит
go test -race.
Пример
res := FetchAll(ctx, []string{
"https://example.com",
"https://example.com/404",
"not a url",
}, 2)
// res[0].StatusCode == 200, res[1].StatusCode == 404, res[2].Err != nil
func FetchAllDedup(ctx context.Context, urls []string, k int) []Result
- Ссылки в списке повторяются. На каждую уникальную ссылку уходит ровно
один запрос, а результат копируется во все её позиции.
- Порядок и длина результата — как у
FetchAll, лимит k тот же.
Устно: как сделать то же без ограничения k и на двух ссылках, какой
вариант проще? Почему здесь не стоит использовать errgroup с отменой по
первой ошибке?
Разбор и эталонное решение — после своей попытки: на собеседовании подсказок не будет.
Сначала попробуй сам — подсказки не дают готовый ответ.