当多个线程共享一个小代理池时,随机挑选条目可能让两个正在进行的操作拿到同一个代理。队列可以保存当前可借出的条目:

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 以原子方式取出一个可用条目。池子耗尽时它会等待,而不是反复扫描共享标志。如果五秒内没有条目到达,queue.Empty 会抛给调用方;请针对该结果选择应用的重新尝试、失败或取消策略。

finally 块在成功或抛出异常后归还已获取的条目。它并不宣称失败的端点就是健康的。隔离、替换和冷却策略需要一条明确的路径,而不是静默丢弃每个遇到临时错误的代理。

对相同 URL 字符串去重可以避免队列中出现重复条目。不同的 URL 仍可能指向同一个物理代理或同一个出口;如果排他性必须作用于该共享资源,就要有意识地把它们归为一组。队列协调的是本进程内的线程,而不是其他机器上的工作进程。

相关文章