用队列在 Python 线程之间共享代理池
使用同步队列和 finally 块为同步线程操作预留一个代理,随后再归还。
当多个线程共享一个小代理池时,随机挑选条目可能让两个正在进行的操作拿到同一个代理。队列可以保存当前可借出的条目:
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 仍可能指向同一个物理代理或同一个出口;如果排他性必须作用于该共享资源,就要有意识地把它们归为一组。队列协调的是本进程内的线程,而不是其他机器上的工作进程。