1. 50,000并发这个数字,先把口径说清楚
先聊个很多团队都踩过的坑:老板要求"并发数到5万",开发测出来对不上,测试说压测机已经撑不住了,运维说监控里连接数根本没那么高。最后发现,大家嘴里说的"并发"压根不是一个东西。
我见过太多团队把"并发连接数"和"QPS"混为一谈。Nginx官网那句"单机支持百万并发连接"说的是连接数,是TCP连接建好之后挂在那里的数量;而高并发业务场景里大家真正关心的往往是吞吐,单位时间能处理多少请求。这两个指标在Nginx的世界里差异巨大。
就拿50,000并发这个目标来说,如果5万指的是"同时有多少个TCP连接保持在Nginx上",那这本身谈不上多极限,一台配置合理的机器完全扛得住;但如果5万指的是"每秒钟要处理5万个请求",那这叫50,000 QPS,难度完全不是一个量级。这篇文章讲的"50,000并发",我按互联网行业最常见的口径来定义:同时在线的客户端连接数,以及在这些连接上持续发生的请求流量,也就是一个比较真实的电商大促、抢票或直播弹幕场景。
那50,000并发连接下Nginx要面对什么?假设这些连接里有动态请求也有静态资源,每个连接平均每10秒发起一次请求,那么Nginx每秒要处理的请求数大约是50,000除以10,等于5,000 QPS。这个量级对Nginx本身来说非常轻松,真正的瓶颈在于内存、文件描述符、后端服务吞吐和内核网络参数。很多人一听到5万并发就想着要上多少台机器,实际上过早引入分布式反而是过度设计。
给新手一个基准感受:一台配置合理的Nginx服务器,只要内核参数和配置项调到位,静态文件场景下支撑十几万并发连接是常态,动态请求要看后端聚合能力。所以别被"5万"这个数字唬住,Nginx能处理,前提是你要知道它背后的工作方式,以及把操作系统级别的限制解开。
本文的阅读对象是那些已经会用Nginx做基本反代和静态服务、但遇到高并发场景就心里没底的同学。我会把Nginx能扛高并发的原理拆开讲清楚,再把从系统层到配置层的调优步骤完整走一遍,最后结合压测工具讲怎么验证"真5万"而不是"纸面5万"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 事件驱动模型才是Nginx最核心的武器
2.1 从Apache的"一连接一线程"说起
要理解Nginx为什么能支撑超高并发,得先知道传统服务器是怎么被并发打垮的。Apache经典的prefork模式,来一个连接就fork一个进程去处理,这个进程在那个连接的生命周期内一直占着不放。如果5万连接同时进来,Apache就要创建5万个进程,光进程上下文切换就会把CPU耗尽,内存更是扛不住,每个进程几MB到几十MB,5万乘以几十MB根本不敢算。
改进版的worker模式采用线程池,比进程轻量一些,但本质上还是一连接一线程或者一连接一书签轮询,线程数量一旦上去,上下文切换和锁竞争照样拖垮性能。这个模型更适合连接数少、每个连接处理时间长的场景。
Nginx走的是完全不同的路线。我第一次看Nginx源码里那个ngx_event_accept的处理逻辑时,印象最深的是它一个工作进程能同时管理成千上万个socket,靠的不是创建无数线程,而是事件驱动。
2.2 epoll:内核帮你盯着所有连接
Nginx在Linux上默认使用epoll,这是Linux内核提供的高性能I/O多路复用机制。通俗理解:你有一个前台(Nginx worker进程),接待大厅里有几十万个客人(socket连接)。如果每来一个客人都要前台亲自招待,那前台累死也干不完;epoll相当于给前台配了几十个"事件传感器",哪个客人招手(数据到达)、哪个客人要结账(连接关闭)、哪个客人进门(新连接),传感器实时汇报,前台只需要一个个处理有事件发生的人,没人找他的时候就休息。
用技术语言说,epoll维护了一个就绪事件列表,Nginx通过epoll_wait批量获取当前有事件发生的文件描述符,然后逐个处理。处理过程里所有I/O都是非阻塞的,读数据时如果内核缓冲区暂时没数据,立刻返回而不是傻等;写数据时如果socket发送缓冲区满了,先登记一个"可写事件",等缓冲区有空间了内核再来通知。这样worker进程永远不会被某一个慢连接卡住。
对比一下子就清晰了:阻塞式I/O模型下,5万连接可能得几万个线程;事件驱动模型下,几个worker进程就够了。Nginx默认worker_processes通常设置为CPU核心数,16核机器跑16个worker进程,每个进程单线程处理所有连接的事件,这就是5万并发的底气所在。
2.3 accept锁和惊群问题
用了epoll之后,还有一个细节很关键——惊群。多个worker进程都阻塞在epoll_wait上,一个新连接到达时,所有worker都会被唤醒去争抢accept,但最后只有一个能成功,其他进程白白被唤醒一轮,这就是惊群效应。
Nginx老版本用accept_mutex(accept锁)来解决这个问题:多个worker轮流持有锁,只有持锁的worker才会把新连接对应的socket加入自己的epoll监听列表。这样做牺牲了一点点多核并行度,但避免了无谓的进程唤醒开销。Nginx后来引入了EPOLLEXCLUSIVE标志,内核只在等待队列里唤醒一个进程,惊群问题进一步缓解。所以你在新版本Nginx里看到accept_mutex on;,默认开启,一般不用动。
不过这里有个坑:惊群问题主要集中在监听socket上的新连接事件,已经建立的连接上的读写事件本来就会精准分发到对应的worker,不存在惊群。所以不要为了追求极致的并发连接数,盲目把worker_processes调到特别大,超过CPU核心数反而会因为频繁上下文切换降低性能。
