여러 스레드가 작은 프록시 풀을 공유할 때 무작위로 항목을 고르면 두 개의 활성 작업에 같은 프록시가 배정될 수 있습니다. 큐는 현재 빌려 쓸 수 있는 항목을 보관할 수 있습니다:

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이 들어 있습니다. 공개 후보는 무료 프록시 목록에서 확인할 수 있습니다. fetch_one이 지원하는 프로토콜과 URL 형식을 사용하세요. 기존의 동기 fetch_one 함수는 반환하기 전에 네트워크 작업을 끝내고 응답을 닫아야 합니다. 그렇지 않으면 연결이 아직 그 프록시를 쓰는 동안 임대가 끝나 버립니다.

Queue.get은 사용 가능한 항목 하나를 원자적으로 제거합니다. 풀이 고갈되면 공유 플래그를 반복해서 훑는 대신 기다립니다. 5초 안에 항목이 도착하지 않으면 queue.Empty가 호출자에게 전달됩니다. 그 결과에 대해 애플리케이션의 재시도, 실패 또는 취소 정책을 선택하세요.

finally 블록은 성공하거나 예외가 발생한 뒤 획득한 항목을 반환합니다. 실패한 엔드포인트가 건강하다고 선언하지는 않습니다. 격리, 교체, 냉각 정책에는 명시적인 경로가 필요하며, 일시적 오류를 본 모든 프록시를 조용히 버려서는 안 됩니다.

동일한 URL 문자열을 중복 제거하면 큐에 중복 항목이 생기지 않습니다. 서로 다른 URL이 같은 물리적 프록시나 같은 출구를 가리킬 수 있으므로, 그 공유 자원에 배타성이 필요하다면 의도적으로 묶으세요. 큐는 이 프로세스의 스레드를 조정하며 다른 머신의 워커를 조정하지 않습니다.

관련 글