Когда несколько потоков делят небольшой пул прокси, выбор случайной записи может дать двум активным операциям один и тот же прокси. Очередь может хранить записи, доступные для заимствования в данный момент:

from contextlib import contextmanager
from queue import Queue

available = Queue()
for endpoint in dict.fromkeys(proxy_urls):
    available.put(endpoint)

@contextmanager
def borrow_proxy():
    endpoint = available.get(timeout=5)
    try:
        yield endpoint
    finally:
        available.put(endpoint)

def process_url(url):
    with borrow_proxy() as endpoint:
        return fetch_one(url, endpoint)

Постройте очередь один раз, до запуска рабочих потоков. proxy_urls содержит полные URL конечных точек. Публичные кандидаты доступны в списке бесплатных прокси; используйте протокол и формат URL, которые поддерживает fetch_one. Существующая синхронная функция fetch_one должна завершить сетевую операцию и закрыть любой ответ до возврата. Иначе заимствование закончится, пока соединение ещё использует этот прокси.

Queue.get атомарно удаляет одну доступную запись. Исчерпанный пул ждёт, а не многократно сканирует общие флаги. Если за пять секунд запись не поступит, queue.Empty дойдёт до вызывающего кода; выберите политику повторных попыток, отказа или отмены приложения для этого результата.

Блок finally возвращает полученную запись после успеха или исключения. Он не объявляет отказавшую конечную точку здоровой. Политики карантина, замены и остывания требуют явного пути, а не тихого отбрасывания каждого прокси, увидевшего временную ошибку.

Удаление дубликатов одинаковых строк URL предотвращает повторные записи в очереди. Разные URL всё ещё могут указывать на один и тот же физический прокси или выход; группируйте их намеренно, если исключительность должна применяться к этому общему ресурсу. Очередь координирует потоки в этом процессе, а не работников на других машинах.

Похожие статьи