with ThreadPoolExecutor(max_workers=20) as ex:
    for r in ex.map(fetch, urls):
        handle(r)

抓取脚本把线程数从二十加到两百,QPS 只涨了一点,随后开始大量连接超时,机器负载升到八以上,错误率明显抬头。这类现象通常不是突然出现的,往前翻半天日志能看到苗头。

追下去是这么回事:每个线程都要占栈内存并参与调度,线程数远超下游能承受的并发,上下文切换和目标端限流一起把收益吃掉,延迟被排队放大。

「线程数过多」确认了原因之后,改动尽量只碰那一处,顺手改的东西后面最容易出问题。监控和日志的时间要对上,时区不一致会多绕很大一圈。

临时改动用注释写上日期和原因,过两周自己都记不清为什么这么写。

处理上按这个顺序:先压测找出下游能接受的最佳并发,不要凭感觉加线程;用固定大小的线程池配合有界队列把总量控制住;超出的请求就快速失败或者退避,不要无限排队。

确认没问题再收工,同一个目标下 QPS 稳定,超时率和负载都回落,错误率也降了下来。

作者 9823189