01-Nginx核心架构与工作原理:进程模型、并发处理、适配高并发售货柜业务
作者:黒漂技术佬
系列专栏:Nginx高可用部署与三端项目实战
一、Nginx是什么?一句话先立住人设
Nginx 是一个用 C 语言写成的高性能 HTTP 服务器和反向代理服务器,同时还能干邮件代理(IMAP/POP3)和 TCP/UDP 代理的活儿。
说人话:它是你服务器机房里那个"前台保安兼调度员",所有从外面打过来的 HTTP 请求,都先到它这儿报到,它再按规矩把请求分发给后面真正干活的微服务。
为什么整个互联网都在用它?因为它有三个别人比不了的优点:
- 快:单机几万甚至十几万并发连接不在话下
- 省:内存占用极低,一万并发连接撑死吃十几兆内存
- 稳:配置改完
reload一下就行,不用停服,在线上就是命
二、为什么Nginx这么快?核心就是"进程模型+事件驱动"
要理解 Nginx 为什么快,得先看看它的老前辈 Apache 是怎么干活的。
Apache 的传统模型:一个请求一个线程
Apache 默认用的是prefork 模式——每来一个连接,就 fork 一个进程来伺候。后来进化出worker 模式,改成一个连接一个线程。但不管怎么变,本质都是"一个请求独占一个执行单元"。
问题来了:这个执行单元大部分时间在干嘛?——在等。等网络数据从网卡到内核,等用户把请求体发完,等后端把响应吐回来。这个"等"的过程中,线程是占着茅坑不拉屎的,白白占着内存和 CPU 调度资源。
并发一上来,几千个线程同时在那儿傻等,CPU 光在上下文切换上就累得够呛,哪还有力气处理真正的业务。这就是 Apache 在高并发下被 Nginx 秒杀的根本原因。
Nginx 的破局思路:别傻等,去干别的
Nginx 的核心哲学就一句话:IO 等待的时间,绝不能浪费,必须用来处理别的连接。
怎么做到的?靠的是两板斧:
- Master-Worker 进程模型——少量进程管大量连接
- 事件驱动 + epoll——一个进程同时盯着一万个连接,谁有数据就处理谁
下面把这两板斧拆开讲透。
三、Master-Worker 进程模型详解
Nginx 启动后,内存里会有一堆进程,但角色分得很清楚:
[root@nginx ~]# ps -ef | grep nginx root 1234 1 0 10:00 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx nginx 1235 1234 0 10:00 ? 00:00:00 nginx: worker process nginx 1236 1234 0 10:00 ? 00:00:00 nginx: worker process nginx 1237 1234 0 10:00 ? 00:00:00 nginx: worker process nginx 1238 1234 0 10:00 ? 00:00:00 nginx: worker process1. Master 进程:老板,只管不干活
Master 进程是 Nginx 的"大管家",由 root 用户启动(因为要绑 80 端口这种特权端口)。它自己不处理任何业务请求,专门干管理上的活儿:
- 启动时读取
nginx.conf,校验配置合法性 - 创建、监控、管理一堆 Worker 进程
- 接收外部信号:
reload(重载配置)、reopen(重开日志)、stop(优雅停止)、quit(立即停止) - 当 Worker 挂了,Master 负责重新拉起一个补上
为什么设计成 Master 不干活?因为老板要是亲自下场搬砖,万一搬砖搬崩了,整个 Nginx 就全挂了。职责隔离——管理进程不碰业务请求,业务进程挂了有大管家兜底重启,这才是高可用的根基。
2. Worker 进程:打工人,干所有的活
Worker 进程是真正干活的。有几个 Worker 由worker_processes配置决定,通常设置为等于 CPU 核心数(比如 4 核就开 4 个 Worker)。
每个 Worker 进程内部维护一个连接池,能同时处理大量连接。关键点来了——Worker 之间是平等的、独立的、竞争式的:
- 它们共享同一份监听 socket(由 Master 创建并 fork 继承)
- 新连接进来时,多个 Worker 通过"竞争 accept"机制抢着处理
- 抢到哪个 Worker 处理,后续这个连接的读写都由它负责,全程不会被别的 Worker 插手
这种"多个平等打工人抢活干"的设计,天然就是负载均衡——谁闲谁接活,不用老板操心分配。
┌──────────────────┐ 客户端请求 ──────▶│ Master 进程 │ (只管理,不接客) │ (root 用户) │ └────────┬─────────┘ │ fork + 监控 ┌──────────────┼──────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Worker 1 │ │ Worker 2 │ │ Worker N │ │ (nginx) │ │ (nginx) │ │ (nginx) │ │ 万级连接 │ │ 万级连接 │ │ 万级连接 │ └──────────┘ └──────────┘ └──────────┘为什么要用多进程而不是多线程?这是 Nginx 设计的精妙之处:
- 进程隔离更稳:一个 Worker 出 bug 崩了,别的 Worker 照常工作,对外几乎无感知
- 避免锁竞争:多线程共享内存得加锁,锁在高并发下是性能毒药;多进程各自独立内存,无锁无争用
- C 语言没有语言级并发安全保护:多进程模型规避了 C 在多线程并发上的坑,简单可靠
四、事件驱动与 epoll 机制
Worker 进程能同时管几万个连接,靠的就是事件驱动。
打个比方:Apache 的做法像给每个客人配一个服务员,服务员得全程陪着,客人想一会儿说一句话,服务员就傻站着等。Nginx 的做法像一个大堂经理,同时盯着大厅里几万个客人,谁举手了(有数据到了)就过去处理谁,处理完立马回前台继续盯着。
"盯着几万个客人"这个动作,在 Linux 下靠的是epoll系统调用。
select/poll 的痛点:每次都全量扫描
早期的 IO 多路复用用select或poll,机制是:每次调用都把所有监听的 socket 全量告诉内核,内核遍历一遍看哪些有事件,再返回。连接数一多,这个遍历就是 O(n) 的灾难——一万连接每次都扫一遍,CPU 直接累趴。
epoll 的破局:回调机制,谁有事通知谁
epoll 是 Linux 2.6 引入的,三个核心 API:
epoll_create:创建一个 epoll 实例(内核里维护一棵红黑树+一个就绪链表)epoll_ctl:把要监听的 socket 注册进去(红黑树插入节点)epoll_wait:阻塞等待,只有就绪的连接才会被返回
关键在 epoll 内部用了回调机制:每个注册的 socket 在内核里挂了个回调,网卡收到数据触发硬件中断后,内核把该 socket 对应的回调一调,把这个 socket 塞进就绪链表。epoll_wait返回时直接拿就绪链表里的内容——活跃连接有多少返回多少,跟总连接数无关,复杂度是 O(活跃数)。
这就是 Nginx 单个 Worker 能扛几万甚至十万连接的底层密码:连接再多,真正活跃的就那一小撮,epoll 只处理活跃的,不浪费 CPU。
五、异步非阻塞 IO
epoll 解决了"高效感知哪个连接有事",接下来是"处理这个事"。
Apache 的同步模型:处理一个请求时,调后端、读文件、写响应,全程阻塞在当前线程上,干等结果回来。
Nginx 的异步非阻塞模型:处理请求时不会傻等。比如反向代理到后端 Tomcat,Nginx 发出请求后立刻返回去处理别的连接,等后端响应到了,epoll 会再次通知 Nginx “这条连接有数据可读了”,Nginx 才回来接着处理。
这种"事件来了我处理,事件没来我去管别人"的模式,让 Worker 进程的 CPU 利用率拉满,全程几乎没有"傻等"的空转。
六、连接数与并发处理能力
Nginx 的并发能力由两个配置项决定:
worker_processes:Worker 进程数,一般等于 CPU 核数(或设auto自动匹配)worker_connections:每个 Worker 能同时持有的最大连接数,默认 1024,生产环境常调到 10240 或更高
理论上最大并发 = worker_processes × worker_connections。比如 4 核机器配worker_connections 10240,理论并发 40960。
但要注意一个细节:Nginx 作为反向代理时,每个客户端连接会对应一条到后端的连接,所以实际可服务的客户端连接数大约是worker_processes × worker_connections / 2(还要留一部分给其他用途)。
调大worker_connections时,操作系统层面的文件描述符限制(ulimit -n)也得跟着调大,不然 Nginx 会报accept() failed (24: Too many open files)这种错。
七、无人售货柜高并发场景:为什么选 Nginx
回到业务。无人售货柜有典型的早高峰/午高峰流量尖刺:
- 早 7:30-9:00 写字楼附近几百台柜子,几万用户同时扫码开门
- 午 11:30-13:00 同一波高峰,外加订单生成、支付回调集中爆发
- 三端流量都汇聚到同一套后端:小程序、安卓工控设备、SaaS 管理后台
这种场景下选 Nginx 的理由:
- 扛尖刺:高峰期瞬时几万 QPS,Nginx 单机轻松扛住,不至于在网关层就成瓶颈
- 省资源:部署在云服务器上,内存占用十几兆,相比 Java 网关动辄几百兆,省钱省实例
- 三端统一入口:小程序 HTTPS 接口、设备 WebSocket 长连接、后台静态资源,全在 Nginx 一层搞定路由分发
- 优雅热重载:业务配置改了
nginx -s reload瞬间生效,连接不中断,凌晨不用熬夜发版 - 稳定性极强:C 写的单体二进制,没有 JVM 那种 GC 停顿和内存泄漏烦恼,常年轻松跑满 99.99% 可用率
一句话:无人售货柜要的是"高并发下不抖、不卡、不贵",Nginx 在网关这层就是最优解。
八、小结
这一篇我们把 Nginx 拆解到了骨子里:从 Apache 的"一请求一线程"痛点,到 Nginx 的 Master-Worker 进程模型职责隔离;从 epoll 回调机制解决"万连接高效感知",到异步非阻塞让 CPU 不空转;最后落到无人售货柜早高峰场景,说清了为什么这个业务必须把 Nginx 放在流量第一层。后面几篇,我们会基于这套架构,把三端流量设计、环境搭建、配置实战一步步展开。