高并发之nginx架构

根据:单机并发简介https://blog.csdn.net/p731heminyang/article/details/154754364https://blog.csdn.net/p731heminyang/article/details/154754364

在硬件性能有限的早期,C10K 问题(即单机如何高效处理上万并发连接)成为网络服务架构的一大挑战。为应对这一瓶颈,操作系统和应用程序层面相继引入了多项关键优化------从非阻塞 I/O、I/O 多路复用(如 epoll、kqueue),到事件驱动模型和高效的内存管理机制。

如今,Nginx 已成为高并发场景下的事实标准,广泛应用于全球各大互联网系统。它之所以能轻松支撑数万乃至数十万并发连接,正是深度结合了现代硬件特性与操作系统能力的结果。通过深入理解 Nginx 在硬件与系统层面的设计与优化策略,我们不仅能掌握其高性能的根源,也能更好地将其应用于实际工程实践。

一、简介

Nginx(发音为 "engine-x")是一款高性能、轻量级的开源 Web 服务器和反向代理服务器,同时也支持负载均衡、HTTP 缓存、流媒体传输等功能。它最初由俄罗斯开发者 Igor Sysoev 于 2004 年发布,旨在解决 C10K(即同时处理一万个并发连接)问题。如今,Nginx 已成为全球最流行的 Web 服务器之一,广泛应用于高并发、高可用性的互联网服务架构中。

1.1 特性

  • 高并发、低内存消耗:采用事件驱动、异步非阻塞的架构模型(如 epoll/kqueue),能高效处理大量并发连接。
  • 反向代理与负载均衡:支持 HTTP、HTTPS、TCP/UDP 等协议的反向代理,并提供多种负载均衡算法。
  • 静态文件服务:高效处理静态资源(如 HTML、CSS、JS、图片等)。
  • 模块化设计:功能通过模块实现,核心模块 + 第三方模块灵活扩展。
  • 热部署与平滑升级:支持不停机更新配置或升级版本。
  • 缓存机制:可作为 HTTP 缓存服务器,提升响应速度。
  • 安全控制:支持访问控制、限速、SSL/TLS 加密等安全功能。

二、历史背景

  • 开发者:Igor Sysoev(俄罗斯工程师)
  • 动机:解决 C10K 问题(即单机处理 10,000+ 并发连接)
  • 灵感来源:Apache 在高并发下的性能瓶颈(每个连接一个进程/线程)
  • 首次公开发布2004 年 10 月 4 日 (版本 0.1.0

初始版本功能:

尽管是早期版本,但已具备 Nginx 核心架构和基础能力:

核心架构

  • Master-Worker 多进程模型
  • 事件驱动 + 非阻塞 I/O (基于 kqueue / select,Linux 下尚未用 epoll)
  • 配置文件驱动nginx.conf

基础 HTTP 功能

  • 静态文件服务(HTML、图片等)
  • 虚拟主机(server 块,基于 IP 或端口)
  • 基本的 MIME 类型支持
  • 日志记录(access log)

三、核心架构分析

这里关于业务逻辑就不分析了,只分析下他是如何做到高并发的,由于不讲业务逻辑部分,所以会比较简单。

为了达到c10K,进行了如下优化:

  • nginx选取了性能最高的C语言开发尽可能的发挥硬件性能
  • 为了更好地适应任务调度,采用了Master-Worker 多进程模型
  • 避免阻塞和轮询,采用了事件驱动 + 非阻塞 I/O(初版为select/poll ,这里直接跳过为epoll)
  • 文件操作,采用零拷贝技术,直接硬盘→内核→socke(网卡)不经过用户态,减少内存使用和cpu计算

3.1 Master-Worker 多进程模型

Master 进程:1 个,由启动用户(如 root)运行

Worker 进程:N 个,默认等于 CPU 核心数(worker_processes auto;)

Master-Worker模型

Master 进程(管理者):

  • 读取并验证配置文件(nginx.conf)
  • 绑定监听端口(如 80/443),创建监听 socket
  • fork 出多个 Worker 子进程
  • 接收并处理外部信号:
  • SIGHUP:重载配置(reload)
  • SIGUSR1:重新打开日志文件(log rotate)
  • SIGQUIT / SIGTERM:优雅关闭或强制退出
  • 监控 Worker 状态:若 Worker 异常退出,可自动重启(取决于配置)

Worker 进程(工作者):

  • 继承 Master 创建的监听 socket
  • 每个 Worker 独立运行一个事件循环(event loop)
  • 使用 epoll(Linux)、kqueue(BSD)等机制监听所有连接
  • 异步、非阻塞地处理 HTTP/TCP 请求
  • 完全独立:进程间无共享内存(除只读配置外),避免锁竞争

为什么采用多进程而非多线程:

对比项 多进程(Nginx) 多线程(Apache prefork/worker)
隔离性 进程崩溃不影响其他 Worker 线程崩溃可能导致整个进程挂掉
内存开销 稍高(但 COW 优化) 较低,但线程栈仍需内存
编程复杂度 无共享状态,无需锁 需处理线程同步、锁竞争
扩展性 天然利用多核 受限于 GIL(某些语言)或锁瓶颈
稳定性 极高 中等

Nginx 选择 多进程 + 单线程事件循环,是为了在性能、稳定性和简洁性之间取得最佳平衡。

3.2 完整流程-epoll(epoll 与 Master-Worker 的协同机制)

Master-Worker模型结合epoll模型,完美的把硬件的性能提升上来,下面是流程

在 Nginx 中,epoll 并非由 Master 进程使用,而是完全运行于每个 Worker 进程内部。这种设计实现了"控制面"与"数据面"的彻底分离:

  • Master 负责管理(Control Plane):不碰任何网络 I/O,只做进程调度与配置加载;
  • Worker 负责处理(Data Plane):每个 Worker 独立持有 epoll 实例,全权处理连接生命周期

请求处理全流程(结合 epoll):

  1. 启动阶段

    • Master 创建监听 socket(如 0.0.0.0:80),但不进入事件循环
    • fork 出 N 个 Worker 进程,所有 Worker 继承该监听 socket 的文件描述符(FD)
  2. 连接到来

    • 内核接收到 TCP SYN 包,完成三次握手,将新连接放入 accept 队列。
    • 所有 Worker 的 epoll 实例均监听该监听 FD,因此可能多个 Worker 被唤醒(惊群问题)。
  3. accept 竞争

    • 若启用 accept_mutex on(旧版默认),仅一个 Worker 能抢到互斥锁并调用 accept()
    • 若启用 reuseport(推荐),内核直接将连接分发给某个 Worker,无竞争、无锁、无惊群
  4. 注册新连接到 epoll

    • 成功 accept 后,Worker 将新连接的 socket FD 加入自己的 epoll 实例:

      复制代码
      epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event);

      此后该连接的所有读写事件均由同一个 Worker 异步处理 ,保证请求处理的局部性(cache-friendly)。

  5. 事件驱动处理

    • Worker 调用 epoll_wait() 阻塞等待事件:

      复制代码
      int n = epoll_wait(epfd, events, max_events, timeout);
    • 当有数据可读(如 HTTP 请求到达)或可写(如后端响应返回),epoll 立即返回就绪 FD 列表。

    • Worker 非阻塞地读取请求、解析、代理、生成响应、写回客户端,全程无 sleep、无阻塞。

  6. 连接复用或关闭

    • 若为 Keep-Alive 连接,FD 继续保留在 epoll 中,等待下一次请求;
    • 否则,关闭 FD,从 epoll 移除,释放资源。

单个 Worker 可同时监控 数万个 FD ,而 epoll_wait() 的时间复杂度为 O(1) (仅返回活跃事件),彻底摆脱了 select/poll 的 O(n) 扫描瓶颈。

四、Nginx 如何突破单机并发五大瓶颈

结合:让 CPU 忙于计算,而非等待 I/O;让资源用在刀刃上,而非浪费在切换与拷贝

Nginx 的高性能不仅是软件设计的结果,更依赖底层硬件与操作系统的配合:

具体优化如下:

瓶颈类型 传统方案问题 Nginx 优化策略
1. 进程/线程调度 每连接一线程 → 内存爆炸、上下文切换开销大 单线程事件循环 + 多进程 • 每 Worker 无阻塞处理万级连接 • 无线程栈开销(节省 2MB/线程)
2. 内存消耗 每连接占用 MB 级内存 轻量连接结构 • 内核 struct sock ≈ 3--8KB • 应用层无额外线程栈 • 32GB 内存可支撑 200 万+ 空闲连接
3. 文件描述符限制 默认 1024,无法扩展 系统级调优ulimit -n 1000000worker_rlimit_nofile • Linux 支持单进程百万 FD
4. I/O 多路复用效率 select/poll O(n) 扫描,拷贝 FD 集合 epoll 事件驱动 • 内核维护红黑树 + 就绪链表 • epoll_wait() 仅返回活跃 FD • 时间复杂度 O(1),无无效遍历
5. 磁盘 I/O 瓶颈 同步 read/write 阻塞线程 零拷贝 + 异步 I/Osendfile():磁盘 → 网卡,绕过用户态 • aio(Linux AIO 或 thread pool)处理大文件

https://blog.csdn.net/p731heminyang/article/details/154754364由于这里是1w连接(只是连接不是并发,并发包含业务处理,后续篇章讲解),我们通过之前的单机并发 计算,如果调优内核参数:

1、每个连接申请大概16kb内存

2、一个进程大概最多支持1048576个句柄(已经达到百万了,不是瓶颈)

3、由于采用事件模式epoll,事件复杂度为O(1),cpu不是瓶颈没问题(注意连接数,没有数据处理,当然如果是https的连接还涉及到加解密计算那么cpu也会成为瓶颈,这里主要是设计等待io问题)

那么瓶颈主要是在内存,那么16kbx10000 约为157M 就可以搞定1w连接,所以老式电脑完全没问题,当然要注意 "是连接数,不是qps、tps" 这种哈,这种不再单单算一个连接,而是整体业务消耗了。

五、结束:

Nginx 的成功,本质上是 对操作系统能力的极致利用

  • 它没有发明新理论,而是将 epoll、sendfile、mmap、SO_REUSEPORT 等内核特性组合成一套高效流水线;
  • 它摒弃了"为每个连接分配资源"的旧范式,转而采用"事件驱动 + 资源复用"的新模型;
  • 它证明了:单机百万并发并非神话,而是合理架构 + 系统调优 + 硬件升级的必然结果
相关推荐
七夜zippoe13 小时前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
Fcy64814 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满14 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠14 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
Harvey90315 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s
珠海西格电力科技16 小时前
微电网能量平衡理论的实现条件在不同场景下有哪些差异?
运维·服务器·网络·人工智能·云计算·智慧城市
释怀不想释怀16 小时前
Linux环境变量
linux·运维·服务器
zzzsde16 小时前
【Linux】进程(4):进程优先级&&调度队列
linux·运维·服务器
聆风吟º18 小时前
CANN开源项目实战指南:使用oam-tools构建自动化故障诊断与运维可观测性体系
运维·开源·自动化·cann
NPE~18 小时前
自动化工具Drissonpage 保姆级教程(含xpath语法)
运维·后端·爬虫·自动化·网络爬虫·xpath·浏览器自动化