
发布时间:2026-10-04 21:13:41
腾讯云服务器 Nginx 反向代理与 HTTPS 调优:并发上不去的真实原因
正文
客户打电话来说服务器带宽拉满了,让我赶紧升配。我上去先没动带宽,而是跑了一条 nginx -T 看配置,又用 ab 压了几轮,结果发现瓶颈根本不在出口带宽,而在 worker_connections 和系统 ulimit 没对齐——连接上限早早顶死,请求排队,表现出来却像"带宽不够"。所以先给结论:腾讯云服务器 Nginx 调优,先把连接数的账算清,再去动带宽和压缩。
反向代理是个典型的"看着配置简单、坑都在细节里"的服务。这篇我不讲安装,按真实排查顺序把并发上不去的几个真凶过一遍:worker 与连接数、upstream 长连接、压缩与缓存头、TLS 会话复用、真实 IP 透传、502/504 的定位,以及安全组和 CDN 回源的边界。顺序基本就是我现场排障的顺序。
并发上不去,先别怪带宽:worker 与连接数的账
Nginx 处理并发靠 worker 进程,每个 worker 能同时持有的连接数由 worker_connections 决定,而 worker 能打开的文件描述符上限又受系统 ulimit -n 甚至 worker_rlimit_nofile 限制。这三个数是有乘法关系的:理论最大并发大致等于 worker_processes × worker_connections,但如果系统的 nofile 比 worker_connections 还小,那么再大的 worker_connections 也是写在纸上的,实际会被系统卡住。
所以调优第一步是核对这三个数能不能对得上:worker_processes auto 让它跟随 CPU 核数;worker_connections 设成几千;同时查 ulimit -n,把系统或 systemd 的限制提上去,或者在 Nginx 里直接用 worker_rlimit_nofile 声明。只改 Nginx 配置不动系统限制,是最常见的一种"改了没效果"。 而且反向代理场景下,一个客户端连接会占用两个连接槽(一个对客户端、一个对上游),算容量时要把这个系数带进去,不然算出来的并发会偏乐观。
腾讯云 CVM 的实例规格直接决定了 CPU 核数,也就决定了 worker 数。小规格实例上,把 worker 设得再花哨也拉不动真实并发,这时候该做的是看瓶颈到底在 CPU、连接数还是上游响应时间,而不是一味调参数。
光靠猜没用,调优得有观测手段。开一个 stub_status 的 location,就能实时看到 active connections、reading、writing、waiting 几个数:active 里大部分落在 waiting,说明连接大多是空闲的,瓶颈不在这里;reading 长期偏高,说明请求头读取慢,可能是客户端网络差或者有人在用 Slowloris 那类慢速攻击占连接;writing 堆积,多半是后端响应慢,把连接一直占着不释放。再用 wrk 或 ab 做几轮递增并发的压测,观察每秒请求数和延迟随并发变化的曲线,在哪一段开始变平或者抖动,那里往往就是真正的瓶颈点。有了这条曲线,和客户讨论"到底要不要升配"才有依据,而不是凭感觉。
keepalive 与 upstream 长连接:被忽略的一半
反向代理最常见的性能浪费,是对上游(upstream)不复用连接。默认情况下 Nginx 每转发一个请求,都可能重新和后端建一次 TCP 连接,三次握手加上 TLS 握手(如果后端也是 HTTPS)的开销,全摊到每个请求上。并发一高,后端光在建连上就耗掉大量资源。
解法是在 upstream 块里配 keepalive 32 之类,并在 location 或 proxy_pass 处把协议的 HTTP 版本设为 1.1,同时带上 Connection "",让 Nginx 和后端之间保持长连接复用。这一步很多人只做了一半:设了 keepalive 却忘了把代理的 HTTP 版本提到 1.1,结果长连接根本建立不起来——keepalive 只是"允许并存多少个空闲连接",真正让它生效的是 HTTP/1.1 加正确的 Connection 头。
对客户端的连接侧也有类似的参数 keepalive_timeout,控制空闲长连接保持多久。设得太短,频繁握手影响体验;设得太长,空闲连接占用资源。我会结合业务访问模式来设,而不是抄一个固定值。
压缩与缓存头:gzip、brotli 与静态资源的正确姿势
传输优化里性价比最高的一项,是开启压缩并给静态资源配上强缓存。gzip 是标配,用它压缩文本类响应(HTML、CSS、JS、JSON)能明显减小体积;brotli 压缩率通常更好,但要注意客户端支持情况和服务端模块是否已编译进去,不是所有发行版都默认带。压缩级别也别一味拉满,级别越高 CPU 消耗越大,对已经压过的图片、视频做 gzip 纯属浪费——那些本来就是压缩格式。
静态资源缓存靠响应头。给带指纹(hash)的静态文件设长 max-age 和 immutable,让浏览器长期复用;给 HTML 这类随时可能变的文件设短缓存或不缓存,避免用户看到旧页面。这两者配合的关键是文件名带内容指纹,内容一变文件名就变,缓存自然失效——这是前端构建该做的事,Nginx 只负责把缓存头配好。同时别忘了把 gzip_static 打开,让构建阶段预压缩出的 .gz 文件被直接使用,省下运行时压缩 CPU。
这些都属于"配置一次、长期受益"的调整,风险低、收益直观。还有一个容易被忽略的点:压缩是 CPU 换带宽的交易。如果你的实例 CPU 本就吃紧,把 gzip 级别拉到最高反而可能拖慢响应;在 CPU 和带宽之间找平衡,比盲目拉满更理性。缓存目录如果放在独立盘上,也能减少磁盘 IO 争抢,尤其当宿主机同时在跑数据库时更明显。
HTTPS 调优:TLS 会话复用与 HTTP/2
HTTPS 多了握手成本,但现代 TLS 有很多手段把它摊薄。核心是会话复用:session_cache 和 session_tickets 让客户端再次访问时跳过完整的握手,只走一次简短的恢复流程,延迟和 CPU 都明显下降。对高并发站点,这几乎是最值得开的 TLS 优化。
协议上开 HTTP/2,利用多路复用让多个请求共享一条连接,缓解 HTTP/1.1 队头阻塞;但要注意,如果前面挂了不支持 HTTP/2 的 CDN 或网关,效果会被截断,端到端的链路要一致。密码套件和 TLS 版本也要取舍:TLS 1.3 握手更快更安全,配置时应优先启用,同时关掉老旧的 TLS 1.0/1.1。OCSP stapling 能让服务器代为获取证书吊销状态,省去客户端自己查询的开销,也建议开上。
证书本身也有讲究。用腾讯云签发的证书或自己申请的证书都行,关键是有效期管理和自动续期,别让证书悄悄过期——我见过因为证书过期导致全站 HTTPS 报错的案例,排查时还以为是网络问题。TLS 调优的目标不是参数最花哨,而是在安全底线之上把握手成本压到最低。
真实 IP 透传:X-Forwarded-For 与日志
反向代理和 CDN 之后,后端看到的访问 IP 往往是代理的 IP,而不是真实客户端。做风控、限流、日志分析、故障定位时,丢掉真实 IP 会让一切失真。所以要在代理层设置 X-Forwarded-For、X-Real-IP 这些头部,把客户端 IP 一路透传到后端;后端应用再从这些头部里读取真实 IP 并记录到日志。
这里有个安全点要注意:这些头部是可以被伪造的。如果 Nginx 对外直接暴露,客户端可以自己塞一个假的 X-Forwarded-For。安全做法是用 real_ip 模块,明确声明可信的代理网段(set_real_ip_from),只信任来自这些来源的转发头部,其余一律用自己的直连 IP。这样既拿到了真实 IP,又不被伪造头欺骗。
日志格式也值得定制:把真实 IP、上游响应时间、请求耗时都记进去,排查慢请求时才有据可依。默认的 combined 格式对性能调优来说信息偏少。
502 与 504:超时设置和慢请求定位
502 和 504 是反向代理最常被问的两个错误码。502 Bad Gateway 通常意味着 Nginx 能连上后端,但后端返回了非法响应或者直接断开——常见于后端进程崩溃、端口没监听、后端自己超时。504 Gateway Timeout 则是 Nginx 等后端响应超过了 proxy_read_timeout 之类的时间限制,后端还在算,Nginx 已经不等了。分清这两个码,排查方向完全不同:502 查后端是否活着,504 查后端为什么慢。
超时设置要成套地调:proxy_connect_timeout 管建连、proxy_send_timeout 管发送、proxy_read_timeout 管读取。只调其中一个往往治标不治本,因为真正的瓶颈常常在后端——数据库慢查询、外部接口卡住、锁竞争。这时候该做的是看上游响应时间,而不是无限放大 proxy_read_timeout,那只会把资源拖住更久。
慢请求定位靠日志。把 $request_time(从收到请求到返回的总时间)和 $upstream_response_time(后端处理时间)分开记录,就能判断慢在 Nginx 还是慢在后端。再配合把超过阈值的慢请求单独打到一条慢日志里,一目了然。这是我排查线上性能问题时最依赖的一招。
与安全组、CDN 回源的边界
调优到这一步,别忘了网络这条边界。安全组决定了哪些端口能被访问,Nginx 监听端口加放行规则要一一对应;如果前面套了 CDN,那么回源流量走的是 CDN 节点到源站,源站安全组最好只放行 CDN 回源段,而不是对全公网开放,否则防护形同虚设。
CDN 与 Nginx 之间也有配合细节:真实 IP 透传要依赖 CDN 设置的头部,回源协议(HTTP 还是 HTTPS)要和源站监听一致,缓存策略要在 CDN 层和 Nginx 层协调,别两边规则打架导致缓存失效或命中率异常低。跨境业务里,回源延迟受线路影响明显,这部分只讲合规部署与延迟优化,任何号称能"绕过线路限制"的说法都不是技术方案。
下面这张表把并发上不去的常见症状和真实原因放在一起对照。
表面症状 常见误判 真实原因 对应参数或动作
并发上不去 带宽不够要升配 连接数上限被 ulimit 卡住 worker_connections 与 worker_rlimit_nofile
高并发时后端很累 后端机器性能差 未复用与上游的连接 upstream keepalive 与 HTTP/1.1
首次访问偏慢 网络延迟大 未开 TLS 会话复用 session_cache 与 session_tickets
页面体积大 服务器没压缩 gzip brotli 未开 gzip 与 gzip_static
日志里看不到真实客户端 以为是应用问题 代理未透传真实 IP X-Forwarded-For 与 set_real_ip_from
偶发 502 网络抖动 后端进程异常或断连 查后端进程与端口监听
偶发 504 加大超时就好 后端处理确实过慢 看 upstream_response_time 定位
FAQ
Q:带宽没满,为什么 Nginx 并发还是上不去?
A:先查连接数的账:worker_connections 与系统 ulimit -n 是否对得上,反向代理还要算上对上游占用的连接槽。很多"并发瓶颈"其实是连接上限先顶死了,和带宽无关。
Q:upstream 配了 keepalive 却没效果?
A:多半是没把代理的 HTTP 版本提到 1.1,也没设置正确的 Connection 头,长连接根本建立不起来。keepalive 只是空闲连接数上限,HT