引言

Nginx(发音为"Engine X")是由 Igor Sysoev 于 2004 年首次公开发布的高性能 HTTP 服务器和反向代理服务器,最初是为了解决 C10K 问题(即如何让单台服务器同时处理 10,000 个并发连接)而生。

如今,Nginx 已经成为互联网基础设施中最重要的组件之一:据 Netcraft 统计,全球超过 34% 的活跃网站使用 Nginx,包括 Netflix、Dropbox、GitHub 等顶尖互联网公司。

本文将从架构原理出发,结合时序图、配置示例和实战场景,带你全面理解 Nginx 的运作机制。


一、Nginx 的核心架构:Master-Worker 模型

1.1 进程模型

Nginx 采用 Master-Worker 多进程架构,而非传统的多线程模型。

graph TD subgraph Nginx进程模型 M["👑 Master Process 主进程"] W1["⚙️ Worker Process 1"] W2["⚙️ Worker Process 2"] W3["⚙️ Worker Process N"] C["📄 Cache Manager"] CL["📄 Cache Loader"] end M -->|"fork + 管理"| W1 M -->|"fork + 管理"| W2 M -->|"fork + 管理"| W3 M -->|"fork"| C M -->|"fork"| CL Client1["🌐 Client 1"] --> W1 Client2["🌐 Client 2"] --> W1 Client3["🌐 Client 3"] --> W2 Client4["🌐 Client N"] --> W3

Master Process 职责:

  • 读取并验证配置文件
  • 创建、监控和管理 Worker 进程
  • 接收并处理信号(reload、stop、upgrade 等)
  • 平滑升级(热更新)

Worker Process 职责:

  • 实际处理客户端连接(HTTP 请求、TCP/UDP 流量)
  • 每个 Worker 单线程运行,基于事件驱动 I/O 多路复用
  • Worker 数量通常设置为与 CPU 核数相等

1.2 事件驱动模型:为何如此高效?

传统 Apache(prefork 模式)是 one thread/process per connection——每个连接独占一个线程,线程上下文切换开销大,内存消耗高。

Nginx 的 Worker 进程使用 非阻塞 I/O + 事件驱动 架构,通过操作系统提供的多路复用接口(Linux 下为 epoll,BSD 下为 kqueue),单个 Worker 可同时管理数万个连接。

关键区别:

特性Apache(prefork)Nginx
并发模型每连接一进程/线程事件驱动,非阻塞 I/O
内存消耗高(每线程 ~8MB 栈)低(每连接 ~10KB)
C10K 场景容易崩溃或性能骤降轻松应对,性能线性扩展
静态文件一般极快(sendfile 系统调用)
动态内容模块内置处理(mod_php)依赖外部 FastCGI/uwsgi

二、HTTP 请求处理时序

2.1 完整请求生命周期

sequenceDiagram participant C as 客户端 Browser participant NL as Nginx Listener participant NW as Nginx Worker participant US as 上游服务 Upstream C->>NL: TCP SYN 建立连接 NL->>NW: accept() 分发连接 NW-->>C: TCP SYN-ACK C->>NW: HTTP Request GET /api/user HTTP/1.1 Note over NW: 解析请求行、Headers 匹配 server_name 匹配 location 规则 alt 静态文件请求 NW->>NW: 读取本地文件 sendfile 零拷贝 NW-->>C: HTTP 200 OK + Body else 反向代理请求 NW->>US: 转发 HTTP Request US-->>NW: HTTP Response NW-->>C: HTTP 200 OK + Body else 请求被拒绝 NW-->>C: HTTP 403 / 404 / 502 end Note over C,NW: Keep-Alive 复用连接 HTTP/1.1

2.2 Nginx 11 个处理阶段(Phase)

Nginx 将每个 HTTP 请求的处理分为 11 个严格顺序执行的阶段,模块可以挂载到对应阶段注入逻辑:

flowchart TD P1["① POST_READ 读取请求头后"] P2["② SERVER_REWRITE server 级别 rewrite 规则"] P3["③ FIND_CONFIG 匹配 location 配置块"] P4["④ REWRITE location 级别 rewrite 规则"] P5["⑤ POST_REWRITE 限制重写次数"] P6["⑥ PREACCESS 访问前预处理 limit_conn/limit_req"] P7["⑦ ACCESS 权限校验 allow/deny、auth_basic"] P8["⑧ POST_ACCESS ACCESS 阶段后处理"] P9["⑨ PRECONTENT 内容生成前 try_files"] P10["⑩ CONTENT 生成响应内容 proxy_pass、index、static"] P11["⑪ LOG 记录访问日志"] P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> P7 --> P8 --> P9 --> P10 --> P11

三、核心功能详解

3.1 静态文件服务

Nginx 提供极高效的静态文件服务,结合 sendfiletcp_nopushgzip 等特性,可以实现接近理论带宽上限的传输速度。

server {
    listen 80;
    server_name static.example.com;

    root /var/www/html;
    index index.html index.htm;

    # 启用 sendfile 零拷贝传输
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # 静态资源长期缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Gzip 压缩
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
    gzip_min_length 1024;
    gzip_comp_level 6;
}

sendfile 零拷贝 vs 传统拷贝原理:

sequenceDiagram participant App as Nginx 进程 participant Kernel as 内核 participant Disk as 磁盘 participant NIC as 网卡 Note over App,NIC: 传统 read() + write():需在用户态/内核态之间切换 App->>Kernel: read(file_fd, buf) Kernel->>Disk: DMA 读取文件 Disk-->>Kernel: 数据到内核缓冲区 Kernel-->>App: 数据拷贝到用户缓冲区 App->>Kernel: write(socket_fd, buf) Kernel-->>NIC: 数据到 Socket 缓冲区 DMA 发送 Note over App,NIC: sendfile():全程在内核态完成,无需用户态拷贝 App->>Kernel: sendfile(socket_fd, file_fd) Kernel->>Disk: DMA 读取文件到内核缓冲区 Kernel-->>NIC: 内核缓冲区直接到 Socket 缓冲区

3.2 反向代理与负载均衡

反向代理是 Nginx 最常见的使用场景之一——将外部请求转发到内部服务集群。

upstream backend_pool {
    least_conn;
    server 192.168.1.10:8080 weight=3;
    server 192.168.1.11:8080 weight=1;
    server 192.168.1.12:8080 backup;
    keepalive 32;
}

server {
    listen 80;
    server_name api.example.com;

    location /api/ {
        proxy_pass http://backend_pool;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_connect_timeout 5s;
        proxy_send_timeout    60s;
        proxy_read_timeout    60s;

        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

负载均衡策略对比

graph LR subgraph RR["① Round Robin 轮询"] RR_C["Client"] --> RR_N["Nginx"] RR_N -->|"第1次"| RR_S1["Server A"] RR_N -->|"第2次"| RR_S2["Server B"] RR_N -->|"第3次"| RR_S3["Server C"] end subgraph IH["② IP Hash 会话保持"] IH_C1["Client 1 IP:1.2.3.4"] --> IH_N["Nginx"] IH_C2["Client 2 IP:5.6.7.8"] --> IH_N IH_N -->|"hash→A"| IH_S1["Server A"] IH_N -->|"hash→B"| IH_S2["Server B"] end
策略配置指令适用场景
轮询 Round Robin默认,无需指令无状态服务,通用场景
加权轮询weight=N服务器性能不均等
IP Haship_hash;需要会话保持 Session Sticky
最少连接least_conn;长连接、耗时不均匀的请求
随机random;大规模集群
一致性哈希hash $key consistent;缓存友好型服务

3.3 HTTPS / TLS 终止

Nginx 充当 TLS 终止点(TLS Termination),将 HTTPS 流量解密后以 HTTP 转发给内部服务。

server {
    listen 443 ssl http2;
    server_name www.example.com;

    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    add_header Strict-Transport-Security "max-age=63072000" always;

    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 1.1.1.1 valid=300s;

    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 1d;

    location / {
        proxy_pass http://backend_pool;
    }
}

server {
    listen 80;
    server_name www.example.com;
    return 301 https://$host$request_uri;
}

TLS 握手时序:

sequenceDiagram participant C as 客户端 participant N as Nginx TLS终止 participant B as 后端服务 C->>N: Client Hello TLS版本、加密套件列表 N-->>C: Server Hello 选择的套件 N-->>C: Certificate 服务器证书 N-->>C: Server Hello Done C->>C: 验证证书链 C->>N: Client Key Exchange 预主密钥 C->>N: Change Cipher Spec + Finished N-->>C: Change Cipher Spec + Finished Note over C,N: TLS 握手完成,后续通信加密 C->>N: HTTP GET /api/data 加密传输 N->>B: HTTP GET /api/data 明文转发内网 B-->>N: HTTP 200 OK 明文 N-->>C: HTTP 200 OK 加密返回

3.4 location 匹配规则与优先级

location 是 Nginx 配置的核心,掌握其优先级规则至关重要:

server {
    # 优先级 1:精确匹配(=)
    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    # 优先级 2:前缀匹配加 ^~ 修饰符(匹配后不再检查正则)
    location ^~ /static/ {
        root /var/www;
    }

    # 优先级 3:正则匹配区分大小写(~)
    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        include fastcgi_params;
    }

    # 优先级 4:正则匹配不区分大小写(~*)
    location ~* \.(jpg|png|gif)$ {
        expires 30d;
    }

    # 优先级 5:普通前缀匹配(最低)
    location / {
        try_files $uri $uri/ /index.html;
    }
}

匹配优先级决策流程:

flowchart TD Start["收到请求 URI"] --> Exact{"精确匹配? location = /uri"} Exact -->|"命中"| Done["使用此 location 块"] Exact -->|"未命中"| PrefixScan["扫描所有前缀匹配,记录最长命中"] PrefixScan --> HasCarets{"最长匹配带 ^~ ?"} HasCarets -->|"是"| Done HasCarets -->|"否"| RegexCheck["按顺序检查所有正则 location ~ ~*"] RegexCheck -->|"第一个命中"| Done RegexCheck -->|"全部不命中"| UseSaved["使用最长前缀匹配结果"] UseSaved --> Done

四、高级功能

4.1 限流与熔断

防止恶意流量或突发请求压垮服务:

limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

server {
    location /api/ {
        limit_req zone=api_limit burst=20 nodelay;
        limit_conn conn_limit 10;
        limit_req_status 429;
        limit_conn_status 429;

        proxy_pass http://backend_pool;
    }
}

令牌桶限流状态机:

stateDiagram-v2 [*] --> 正常 : 请求到达 正常 --> 正常 : 速率不超限 正常 --> 队列中 : 速率超限且队列未满 队列中 --> 正常 : 令牌补充,请求处理 队列中 --> 被拒绝 : 队列已满 burst 耗尽 被拒绝 --> [*] : 返回 429 Too Many Requests

4.2 缓存(Proxy Cache)

Nginx 可对上游响应进行缓存,大幅减少后端压力:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;

server {
    location /api/products {
        proxy_cache my_cache;
        proxy_cache_key "$scheme$host$request_uri";
        proxy_cache_valid 200 301 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_lock on;
        proxy_cache_use_stale error timeout updating;
        add_header X-Cache-Status $upstream_cache_status;

        proxy_pass http://backend_pool;
    }
}

缓存命中/未命中/过期处理流程:

sequenceDiagram participant C as 客户端 participant N as Nginx Cache participant U as 上游服务 C->>N: GET /api/products N->>N: 查找缓存 Key alt 缓存命中 HIT N-->>C: 200 OK 缓存数据 X-Cache-Status: HIT else 缓存未命中 MISS N->>U: 回源请求 U-->>N: 200 OK + 响应数据 N->>N: 写入本地缓存 N-->>C: 200 OK 新数据 X-Cache-Status: MISS else 缓存过期 EXPIRED N->>U: 条件请求 If-Modified-Since alt 内容未变化 U-->>N: 304 Not Modified N->>N: 刷新缓存过期时间 N-->>C: 200 OK 旧缓存数据 else 内容已更新 U-->>N: 200 OK 新数据 N->>N: 更新本地缓存 N-->>C: 200 OK 新数据 end end

4.3 WebSocket 代理

WebSocket 需要从 HTTP 升级协议,Nginx 需要特殊配置:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    location /ws/ {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

WebSocket 升级握手时序:

sequenceDiagram participant C as 浏览器 participant N as Nginx participant WS as WebSocket Server C->>N: HTTP GET /ws/ Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: xxx N->>WS: 转发 HTTP Upgrade 请求 WS-->>N: HTTP 101 Switching Protocols Upgrade: websocket Sec-WebSocket-Accept: yyy N-->>C: 101 Switching Protocols Note over C,WS: WebSocket 双向通道建立完成 C->>N: WebSocket Frame 客户端数据 N->>WS: 透传 WebSocket Frame WS-->>N: WebSocket Frame 服务端推送 N-->>C: 透传 WebSocket Frame

五、生产环境最佳配置实践

5.1 主配置文件结构

user  nginx;
worker_processes auto;
worker_rlimit_nofile 65535;

error_log /var/log/nginx/error.log warn;
pid       /var/run/nginx.pid;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" rt=$request_time '
                    'urt=$upstream_response_time';

    access_log /var/log/nginx/access.log main buffer=32k flush=5s;

    sendfile    on;
    tcp_nopush  on;
    tcp_nodelay on;

    keepalive_timeout  65;
    keepalive_requests 1000;

    client_max_body_size   10m;
    client_body_buffer_size 128k;

    server_tokens off;

    include /etc/nginx/conf.d/*.conf;
}

5.2 完整的生产级反向代理配置

upstream api_servers {
    least_conn;
    server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:8080 max_fails=3 fail_timeout=30s;
    keepalive 64;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/nginx/ssl/api.example.com.pem;
    ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_session_cache   shared:SSL:10m;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    location = /health {
        access_log off;
        return 200 "OK\n";
        add_header Content-Type text/plain;
    }

    location /api/ {
        limit_req zone=api_limit burst=50 nodelay;
        limit_conn conn_limit 20;

        proxy_pass http://api_servers;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_connect_timeout 3s;
        proxy_send_timeout    30s;
        proxy_read_timeout    30s;

        proxy_next_upstream error timeout http_500 http_502 http_503;
        proxy_next_upstream_tries 2;
    }

    location /static/ {
        root /var/www/api.example.com;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;
}

六、常用运维命令

# 检查配置文件语法
nginx -t

# 平滑重载配置(不中断现有连接)
nginx -s reload
systemctl reload nginx

# 停止服务
nginx -s stop    # 立即停止
nginx -s quit    # 优雅退出

# 查看版本与编译模块
nginx -V

# 实时查看日志
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log

# 查看实时连接状态(需 stub_status 模块)
curl http://localhost/nginx_status
# 输出示例:
# Active connections: 291
# server accepts handled requests
#  16630948 16630948 31070465
# Reading: 6 Writing: 179 Waiting: 106

热升级流程(不停服升级 Nginx 二进制)

sequenceDiagram participant Old as 旧 Master PID=100 participant New as 新 Master PID=200 participant W as Worker 进程 Note over Old: 当前正在提供服务 Old->>New: 发送 USR2 信号 启动新 Master 进程 New->>W: fork 新 Worker 使用新版本二进制 Note over Old,New: 新旧 Master 同时运行 流量平稳过渡 Old->>Old: 发送 WINCH 信号 旧 Worker 优雅退出 Note over New: 仅新 Master + 新 Worker 提供服务 Old->>Old: 发送 QUIT 信号 旧 Master 退出 Note over New: 热升级完成 零停机时间

七、性能调优一览

调优维度配置项推荐值说明
进程worker_processesauto等于 CPU 核数
进程worker_rlimit_nofile65535文件描述符上限
事件worker_connections4096~10240单 Worker 最大连接
事件useepollLinux 最高效事件模型
事件multi_accepton一次接受多个连接
传输sendfileon零拷贝文件传输
传输tcp_nopushon合并数据包减少 RTT
压缩gzip_comp_level6压缩比与 CPU 开销平衡
缓冲client_body_buffer_size128k减少磁盘临时文件 I/O
连接keepalive_timeout65s连接复用时间
上游keepalive32~64上游连接池大小
安全server_tokensoff隐藏版本信息

八、总结

Nginx 之所以能在如此广泛的场景中被采用,核心在于其精妙的设计哲学:

  • Master-Worker 进程隔离:Worker 崩溃不影响 Master,可被自动重启;支持平滑升级,无需停机
  • 事件驱动非阻塞 I/O:单线程轻松管理数万并发连接,内存效率极高
  • 模块化架构:SSL、gzip、缓存、限流等功能均为模块,可按需编译
  • 高度可配置:从简单的静态文件服务到复杂的多层反向代理,一份配置文件搞定

无论你是运维工程师、后端开发者,还是全栈工程师,深入理解 Nginx 都将大幅提升你在网络架构、性能优化和系统稳定性方面的能力。


参考资料