Обработка неудачных запросов через прокси с помощью asyncio.gather
Проверяйте исключения, которые возвращает asyncio.gather, и сохраняйте связь каждого результата с выбранным прокси.
asyncio.gather с return_exceptions=True помещает исключения задач в свой список результатов. Поэтому ожидание пакета без исключения не означает, что каждый запрос через прокси завершился успешно.
Держите пункт назначения и выбранный прокси рядом с отправленными awaitable-объектами, затем проверяйте каждый результат:
outcomes = await asyncio.gather(*jobs, return_exceptions=True)
for (url, proxy), outcome in zip(routes, outcomes):
if isinstance(outcome, BaseException):
record_failure(url, proxy, outcome)
else:
record_success(url, proxy, outcome)
jobs содержит корутины запросов приложения; routes содержит соответствующие им пары (url, proxy) в том же порядке. Функции записи — это собственная обработка результатов приложения. gather сохраняет порядок входа, даже если порядок завершения отличается.
Возвращённый CancelledError дочерней задачи классифицируйте как отмену, а не обвиняйте её прокси. Отмена внешнего gather по-прежнему распространяется на незавершённые дочерние задачи; return_exceptions не превращает эту отмену в обычный успех.
Обработка HTTP-статуса также относится к операции запроса. aiohttp по умолчанию не возбуждает исключение при каждой ошибке HTTP, поэтому возвращайте статус или применяйте raise_for_status согласно политике задачи.
Как только результаты видны, приложение может отличать сбои соединения от ответов пункта назначения и решать, какая работа заслуживает ещё одной попытки.