很多做数据采集、批量提交或高频请求的朋友都有过类似经历:代理IP也买了,带宽也够,机器配置也不差,可并发量就是死活上不去。请求一多,不是超时就是报错,程序动不动卡住。遇到这种情况,多数人第一反应是换代理,但换了之后问题依旧。其实,真正的瓶颈往往不在代理服务端,而在你自己机器上的三层"隐形天花板"——连接池配置、文件描述符上限、操作系统级参数限制。这篇文章就带你把这三层逐个拆开,找到并发上不去的真正原因。
先看症状:并发上不去的三种典型表现
动手调优之前,先对照一下你遇到的是哪种情况,不同症状对应不同的瓶颈层级:
表现一:请求排队,吞吐量上不去。程序没有报错,但请求发出后长时间等待,每秒完成的请求数远低于预期。这通常是连接池太小,请求在池外排队等空闲连接。
表现二:报错 "too many open files"。这是最经典的文件描述符耗尽信号。Linux 系统里每一个网络连接都要占用一个文件描述符,默认上限往往只有 1024,并发稍微一高就撞墙。
表现三:报错 "Cannot assign requested address" 或大量超时。用命令查看会发现系统里堆积了成千上万个 TIME_WAIT 状态的连接,临时端口被耗尽,新连接建立不起来。
这三种表现分别对应连接池、文件描述符和系统端口三个层级,下面逐一给出诊断和解决方案。
第一层:连接池——别让每个请求都从零开始
很多人写采集程序或批量任务时,习惯每次请求都新建一个连接,用完就断。这种"短连接"模式的问题在于:每一次请求都要重新经历 DNS 解析、TCP 三次握手、TLS 握手的完整流程,单个请求多花几十到几百毫秒,请求量一高,这些开销会成倍放大,还容易把代理端的建连额度打满。
连接池的思路完全不同:连接用完不关闭,放回池子里给下一个请求复用。建连的开销只付一次,后续请求直接走现成通道,速度和稳定性都会明显提升。
| 对比项 | 短连接模式 | 连接池模式 |
|---|---|---|
| 建连开销 | 每次请求都重新握手 | 建一次反复复用 |
| 延迟表现 | 波动大,容易超时 | 更低、更稳定 |
| 并发能力 | 容易触顶报错 | 平稳可控 |
| 适用场景 | 低频、零散请求 | 高并发、持续采集 |
以 Python 的 requests 库为例,正确使用连接池的写法是这样的:
import requests
from requests.adapters import HTTPAdapter
session = requests.Session()
adapter = HTTPAdapter(
pool_connections=20, # 连接池个数(按域名拆分)
pool_maxsize=100, # 每个池最大连接数
max_retries=3
)
session.mount('http://', adapter)
session.mount('https://', adapter)
# 后续所有请求都走 session,连接自动复用
resp = session.get('http://example.com/api', timeout=10)
两个参数的设置原则:pool_maxsize 要略高于你的实际并发线程或协程数,否则请求会在池外排队;但也不要盲目调到几千,池子太大白白占用内存,还可能超过代理端的并发配额。另外要确认你所用的 HTTP 客户端是否默认开启了连接复用,很多框架默认走短连接,需要手动开启 keep-alive。
第二层:文件描述符——too many open files 的真凶
连接池调好了,如果还报 "too many open files",那就是文件描述符上限在卡你。Linux 中,每个 socket 连接、每个打开的文件都要消耗一个文件描述符(fd),而系统给每个进程的默认上限通常只有 1024。也就是说,你的程序最多同时维持一千个左右的连接,对高并发业务来说远远不够。
诊断和处理分三步走。
第一步,查看当前上限:
# 查看当前会话的文件描述符上限
ulimit -n
# 查看某个运行中进程实际生效的上限
cat /proc/进程号/limits | grep "open files"
第二步,临时调大(当前会话立即生效):
ulimit -n 65535
第三步,写入配置永久生效:
# 编辑 /etc/security/limits.conf,加入以下两行
* soft nofile 65535
* hard nofile 65535
这里有两个容易踩的坑:一是软限制和硬限制要一起改,软限制是实际生效值,硬限制是软限制能调到的天花板;二是修改后必须重新登录或重启进程才生效。如果你的程序是用 systemd 托管的服务,limits.conf 对它不生效,要在服务配置文件里单独设置:
# 在服务的 .service 文件中添加
[Service]
LimitNOFILE=65535
# 然后重载并重启
systemctl daemon-reload
systemctl restart 你的服务名
第三层:系统限制——端口耗尽与 TIME_WAIT 风暴
解决了前两层,还有最后一道关卡:操作系统层面的 TCP 参数。客户端每建立一个连接,都要占用一个临时端口,而 Linux 默认的临时端口范围大约只有三万个。更麻烦的是,短连接大量关闭后,端口会进入 TIME_WAIT 状态,默认要等两分钟才释放。高并发下端口消耗速度远超释放速度,很快就会无端口可用,报出 "Cannot assign requested address"。
先确认是不是这个问题:
# 统计 TIME_WAIT 状态的连接数
ss -ant | grep TIME_WAIT | wc -l
# 查看当前临时端口范围
cat /proc/sys/net/ipv4/ip_local_port_range
如果 TIME_WAIT 数量上万、端口范围偏窄,就按下面的方案调整:
# 1. 扩大临时端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
# 2. 允许复用 TIME_WAIT 状态的端口(客户端侧安全)
sysctl -w net.ipv4.tcp_tw_reuse=1
# 3. 增大监听队列,避免高并发瞬间丢连接
sysctl -w net.core.somaxconn=65535
# 永久生效:把以上参数写入 /etc/sysctl.conf 后执行
sysctl -p
不过要说明的是,调系统参数是"治标",用长连接减少 TIME_WAIT 的产生才是"治本"。这也是为什么连接池要放在第一位——连接复用做得好,TIME_WAIT 自然就少了,系统参数只需要做兜底保障。
一套可以直接照做的排查流程
最后把前面的内容整理成一套完整的排查顺序,遇到并发上不去就按这个流程走:
第一步,看报错定层级。"too many open files" 优先查文件描述符;"Cannot assign requested address" 优先查端口和 TIME_WAIT;无报错但吞吐低,优先查连接池。
第二步,查连接池配置。确认程序用了连接复用,池大小与实际并发数匹配。
第三步,查文件描述符。用 ls /proc/进程号/fd | wc -l 看当前占用,对照上限决定是否调大。
第四步,查系统端口。统计 TIME_WAIT 数量,必要时扩大端口范围并开启复用。
第五步,逐步加压验证。每次只改一个参数,从小并发开始逐步上调,观察吞吐量和错误率的变化,避免一次改一堆导致无法定位。
客户端之外:代理服务端的并发能力同样关键
把客户端这三层全部调优之后,如果并发还是卡在某个数值上不去,那瓶颈大概率转移到了代理服务端——不同服务商的 IP 池规模、单 IP 并发配额、服务器承载能力差异很大。选服务商时重点看三点:IP 池是否够大且可用率高,池子太小,请求量一高单个 IP 被访问的次数就多,容易触发限制;响应速度是否足够快,高并发下每一毫秒的延迟都会被放大;协议支持是否齐全,HTTP、HTTPS、SOCKS5 全覆盖才能适配各种业务框架。
在这几个维度上,天启HTTP 是国内业务场景下值得优先考虑的选择:自建机房保障线路质量,IP 可用率高,响应速度达到毫秒级,全面支持 HTTP、HTTPS、SOCKS5 协议,覆盖全国多个城市节点,无论是高并发采集、批量任务还是业务监控场景都能稳定承接。具体的并发配额和套餐规格,建议直接咨询天启HTTP官方,根据业务量选择匹配的方案。
常见问题
Q: ulimit -n 改了之后,程序还是报 too many open files?
临时修改只对当前终端会话生效,新启动的进程不会继承。永久生效要写入 /etc/security/limits.conf 并重新登录;如果是 systemd 托管的服务,必须在 service 文件里配置 LimitNOFILE 并重启服务。改完后用 /proc/进程号/limits 验证实际生效值。
Q: 连接池是不是设置得越大越好?
不是。池子过大有三个副作用:占用大量内存、超出代理端并发配额被拒绝、空闲连接被服务端单方面断开反而增加重连成本。正确做法是让 pool_maxsize 略高于实际并发数,再通过压测微调。
Q: 系统里几万个 TIME_WAIT 正常吗?需要处理吗?
高并发短连接场景下这是正常现象,只要临时端口没耗尽就不影响业务。如果已经出现端口分配失败,就开启 tcp_tw_reuse 并扩大端口范围;更根本的解决办法是改用长连接,从源头减少 TIME_WAIT 的产生。
Q: 天启HTTP 支持哪些协议?对接方便吗?
天启HTTP 支持 HTTP、HTTPS、SOCKS5 三种主流协议,Python、Java、Go 等主流语言的请求库和采集框架都可以直接配置使用,拿到代理地址和认证信息后填入即可。
Q: 客户端全部调优了,并发还是上不去怎么办?
此时瓶颈大概率在代理服务端。先确认你当前套餐对应的并发配额,再检查是否因为请求过于集中导致单个 IP 被目标限制。如果服务商的 IP 池规模和承载能力不足,换一家池子更大、可用率更高的服务商(比如天启HTTP),往往能直接解决问题。





