深入剖析Nginx架构及其不同使用场景下的配置

发布于:2025-06-27 ⋅ 阅读:(17) ⋅ 点赞:(0)

一、Nginx 整体架构概览

1. Nginx简介

Nginx 是采用 C 语言 编写的高性能 Web 服务器、反向代理服务器及邮件代理服务器,特点是:高并发、高可用、低内存占用、模块化设计

架构核心理念:

  • Master-Worker 多进程模型

  • 事件驱动(Event-Driven) + 异步非阻塞

  • 高度模块化设计


2. 进程模型

Nginx 的进程模型非常轻量,通常包含:

1. Master 进程

  • 启动时由 shell 进程 fork 出来

  • 主要负责:

    • 读取配置文件

    • 管理 Worker 子进程(启动/重启/关闭)

    • 平滑升级(不间断服务)

2. Worker 进程

  • 实际处理请求的进程

  • 默认情况下每个 CPU 一个 worker(可配置)

  • 每个 worker 独立工作,通过 epoll 等机制异步处理连接

3. Cache Manager/Loader(可选)

  • 用于管理磁盘缓存(如 proxy_cache)

  • 管理缓存的过期与空间

Worker 之间互不通信,避免锁竞争,共享资源通常通过共享内存实现。


3. 事件驱动与异步非阻塞机制

Nginx 使用Reactor 模型 + 非阻塞 IO,事件模型封装成 ngx_event 模块,底层可选择:

  • Linux: epoll

  • BSD: kqueue

  • Windows: IOCP

  • Others: select/poll

每个 worker 维护一个事件循环(event loop),处理以下类型事件:

  • 新连接事件(accept)

  • 读写事件(read/write)

  • 定时器事件(超时)

  • 信号事件

示例事件循环伪代码:

while (true) {
    events = epoll_wait(epoll_fd, timeout);
    for (event in events) {
        if (event == read) {
            handle_read();
        } else if (event == write) {
            handle_write();
        }
    }
    process_timers();
}

4. 模块化设计

Nginx 模块系统非常强大,主程序仅提供框架,几乎所有功能都通过模块扩展。

模块分类

类型 说明
核心模块 与配置、事件循环、进程管理相关
标准模块 官方提供,如 http, gzip, rewrite
第三方模块 社区开发,如 lua-nginx-module, ngx_brotli
事件模块 封装不同操作系统的 IO 模型(epoll/kqueue 等)
过滤模块 对请求或响应进行过滤、转换(如 gzip, chunk)
处理模块 提供服务逻辑,如 ngx_http_proxy_module

模块生命周期 Hook:

模块通过 Hook 函数注册以下生命周期点:

  • postconfiguration

  • init_module

  • init_process

  • init_thread

  • exit_thread

  • exit_process

  • exit_master


5. 请求处理流程

以 HTTP 请求为例,完整流程如下:

1. 请求接收

  • Worker 进程通过 epoll 监听 socket fd 上的 accept 事件

  • 建立连接后封装为 ngx_connection_t

2. 请求解析

  • 使用状态机解析 HTTP 请求头、方法、URI 等

  • 填充 ngx_http_request_t 结构

3. 查找 Location 配置

  • 基于 URI 匹配 location block

  • 加载对应 handler(如 proxy_pass)

4. 模块链处理

Nginx 通过模块链的方式处理请求,每个模块处理一部分逻辑:

HTTP 请求
→ handler 模块
→ access 模块
→ rewrite 模块
→ content 模块(如 proxy)
→ filter 模块(gzip,chunked)
→ output

每个模块通过 ngx_http_module_t 结构中的回调函数插入处理链。

5. 响应输出

  • 各模块处理完响应后输出到客户端

  • 使用 sendfile + writev 实现 零拷贝


6. 内存管理与连接池

Nginx 有自己的一套高性能内存池机制,用于避免频繁 malloc/free:

  • ngx_pool_t: 每个请求创建自己的内存池,生命周期跟随请求

  • 可申请小块内存(<4096)快速分配

  • 请求结束时统一释放内存池,避免内存泄漏

连接相关资源也使用对象池(如 ngx_connection_tngx_buf_t)复用,提高性能。


7. 配置解析系统

Nginx 配置文件是模块驱动的,结构清晰。

配置解析过程:

  1. Master 进程读取配置文件(nginx.conf

  2. 每个模块注册自己的配置指令及解析函数

  3. 创建配置结构体,填入配置参数

  4. 绑定到 ngx_cycle_t

支持嵌套结构、变量引用($host)、include 等高级配置特性。


8. 零拷贝机制(Zero Copy)

为了提升性能,Nginx 避免数据拷贝,使用以下机制:

技术 说明
sendfile() 直接从磁盘文件到 socket,无需中转缓冲区
mmap() 映射文件到内存
writev() 将多个缓冲区一次性写入 socket
splice() 在 pipe 与 socket 之间传输,无需用户态

这些机制显著降低了 CPU 消耗和内存拷贝,提高了吞吐量。


9. 共享内存与缓存管理

Nginx 提供共享内存机制支持:

  • 状态统计模块(如 stub_status)

  • 缓存(proxy_cache, fastcgi_cache)

  • 限流(limit_req, limit_conn)

  • Nginx Plus 状态页

通过 ngx_shm_zone_t 管理共享内存区域,使用 slab 分配器精细管理内存块。


10. 日志系统

Nginx 日志系统也高度模块化:

  • access_log, error_log

  • 格式化配置灵活:log_format

  • 支持异步写入、缓存 flush、缓冲池等

  • 每个请求独立生成 ngx_log_t 对象


11. Nginx 底层架构图示

         ┌────────────────────┐
         │    Master 进程      │
         └────────────────────┘
                   │
      ┌────────────┴────────────┐
      ▼                         ▼
┌─────────────┐         ┌─────────────┐
│ Worker 进程 │  ...    │ Worker 进程 │
└─────────────┘         └─────────────┘
       │ epoll / kqueue / IOCP
       ▼
┌──────────────────────────────────────┐
│       Reactor 模型:事件循环         │
├──────────────────────────────────────┤
│ Connection Pool + 内存池 + 模块链    │
├──────────────────────────────────────┤
│ HTTP / TCP 模块处理,输出响应        │
└──────────────────────────────────────┘

12. 小结

关键模块 技术点
高性能 异步非阻塞 IO,sendfile
高并发 多 worker,事件驱动
模块化 所有功能模块驱动
内存管理 内存池、连接池
高扩展性 插件化模块架构

二、Nginx配置详解并结合使用场景深入分析

1. 主配置(全局配置块)

主配置一般出现在 nginx.conf 的最上层,用于控制整个服务器级别的行为。

常见配置项:

配置项 含义和作用
user 指定 worker 子进程运行的系统用户
worker_processes 启动的 worker 数量,建议设置为 CPU 核心数
worker_cpu_affinity 绑定 CPU,提高性能(用于多核)
error_log 全局错误日志路径及级别(`debug
pid 指定 pid 文件路径
worker_rlimit_nofile 最大文件描述符限制

示例:

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

使用场景分析:

  • 高并发服务:建议 worker_processes 设置为 auto,根据 CPU 自动适配。

  • Debug 排查问题时:可以临时设置 error_logdebug


2. events 块配置(事件模型)

控制 Nginx 的事件处理机制,位于 nginx.confevents 块中。

常见配置项:

配置项 含义和作用
worker_connections 每个 worker 支持的最大连接数(非客户端连接)
use 强制指定事件模型(如 epoll/kqueue)
multi_accept 是否每次尽可能多接受新连接

示例:

events {
    use epoll;
    worker_connections 10240;
}

使用场景分析:

  • 高连接量网站:worker_connections * worker_processes 决定最大并发连接数。

  • 性能调优:使用 epollkqueue(操作系统支持前提下)可显著提升性能。


3. http 块配置(Web 服务核心)

这是 Nginx 配置中最重要的一部分,包含:

  • 虚拟主机配置(server

  • 路由与 location 规则

  • 缓存、代理、gzip 等模块

  • 日志、限速、连接控制等功能


4. server 块(虚拟主机配置)

一个 server 表示一个网站服务,可监听不同端口/域名。

常见配置项:

配置项 含义和作用
listen 指定监听的端口和地址
server_name 指定虚拟主机域名
root 指定 web 根目录
index 默认首页文件
error_page 自定义错误页

示例:

server {
    listen 80;
    server_name www.example.com;
    root /var/www/example;
    index index.html index.htm;
    
    error_page 404 /404.html;
}

使用场景分析:

  • 部署多个站点时,通过 server_namelisten 实现虚拟主机划分。

  • 多端口监听(如 80 和 443)可以写多个 server


5. location 块(请求路由匹配)

定义请求 URI 匹配规则和处理方式,是 HTTP 配置的核心。

匹配类型:

类型 示例 含义
精确匹配 location = /foo 严格等于 /foo 时生效
前缀匹配 location /img/ URI 以 /img/ 开头
正则匹配 location ~ \.php$ 匹配以 .php 结尾的路径

示例:

location / {
    try_files $uri $uri/ =404;
}

location /images/ {
    root /data;
}

location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
}

使用场景分析:

  • 静态资源分流:不同类型资源设置不同缓存头、路径。

  • 正则匹配动态请求,如 PHP、API 等。


6. 反向代理(proxy)

用于将客户端请求转发到后端服务器,是 Nginx 的核心能力之一。

常见配置项:

配置项 说明
proxy_pass 设置代理后端地址
proxy_set_header 设置请求头信息,传递客户端信息
proxy_connect_timeout 连接后端超时时间
proxy_read_timeout 读取响应超时时间
proxy_redirect 修改后端跳转地址

示例:

location /api/ {
    proxy_pass http://backend_server/api/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

使用场景分析:

  • 微服务网关:按路径路由不同服务。

  • 解决跨域问题:配合 add_header 设置 CORS。


7. 负载均衡配置(upstream)

Nginx 作为七层负载均衡器,可配置多台后端服务。

配置示例:

upstream backend {
    server 10.0.0.1:8080 weight=3;
    server 10.0.0.2:8080 max_fails=2 fail_timeout=30s;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

支持算法:

  • round-robin(默认)

  • least_conn

  • ip_hash

  • hash $request_uri consistent;(需第三方模块)

使用场景分析:

  • 动态后端多副本:如服务部署多个 pod 实例,使用 upstream 配置调度。

  • 实现后端故障自动摘除(max_fails, fail_timeout)。


8. 限流与连接控制

请求限速:

limit_req_zone $binary_remote_addr zone=req_limit:10m rate=1r/s;

server {
    location /login/ {
        limit_req zone=req_limit burst=5;
    }
}

并发连接数限制:

limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
    location / {
        limit_conn addr 1;
    }
}

使用场景分析:

  • 防止暴力破解、接口刷爆:对登录、注册接口限速。

  • 限制单个 IP 并发连接,防止 DDoS。


9. 安全控制

拒绝某 IP:

deny 192.168.1.1;
allow 192.168.0.0/16;

强制 HTTPS:

server {
    listen 80;
    return 301 https://$host$request_uri;
}

设置头防御:

add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";

10. 缓存配置

静态资源缓存:

location ~* \.(jpg|png|gif|css|js)$ {
    expires 30d;
    access_log off;
}

反向代理缓存(proxy_cache):

proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=cache_zone:10m inactive=60m;

location /api/ {
    proxy_cache cache_zone;
    proxy_pass http://backend;
    proxy_cache_valid 200 1h;
}

11. 日志配置

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main;

使用场景分析:

  • 业务分析:配合 ELK 分析用户行为。

  • 安全审计:统计恶意请求、慢请求等。


12. 常用变量(部分)

变量名 含义
$remote_addr 客户端 IP
$http_user_agent 客户端 UA
$request_uri 请求 URI
$host 请求的 Host 头
$upstream_addr 实际转发到的后端地址(proxy)

13. 小结:分类速查表

分类 功能
主配置 进程管理、日志、用户
events IO 模型、最大连接数
http/server 网站配置、虚拟主机
location 路由控制、反向代理、限速
upstream 负载均衡、服务池
cache 缓存目录、缓存策略
security 黑名单、头安全、重定向
logging 日志格式与存储路径

三、Nginx如何解决跨域问题

1. 什么是跨域?为什么会出现跨域问题?

1.1 浏览器的同源策略(Same-Origin Policy)

同源策略要求:只有协议、域名、端口都相同,才能互相访问资源

http://example.com:80/api/user ✅
http://example.com:8080/api/user ❌ (端口不同)
https://example.com/api/user ❌ (协议不同)
http://other.com/api/user ❌ (域名不同)

1.2 跨域行为触发场景

当以下前端行为访问非同源资源时,会触发跨域限制:

  • 使用 XMLHttpRequest, fetch, axios 发起请求

  • 请求类型为非简单请求(如 PUT, DELETE, application/json

  • 请求带有自定义头(如 Authorization

  • 页面中嵌入 <iframe><img><script> 等外部域资源


2. 跨域类型与浏览器行为

2.1 简单请求

满足以下条件被认为是“简单请求”:

  • 方法为 GET / POST / HEAD

  • 请求头不超出:

    • Accept, Accept-Language, Content-Language, Content-Type(必须是表单类型)

  • 不带 Authorization、自定义 headers 等

浏览器行为:

直接发请求 + 检查响应头是否包含允许跨域的信息(如 Access-Control-Allow-Origin


2.2 非简单请求(复杂请求)

如:

axios.post("http://api.server.com/data", data, {
  headers: { "Content-Type": "application/json" },
});

浏览器行为:

  1. 先发一条 OPTIONS 请求(预检请求,Preflight)

  2. 服务端必须响应对应的 CORS 头信息

  3. 如果 OPTIONS 返回成功,再发真正的业务请求


3. Nginx 解决跨域的原理与配置

Nginx 本身不受跨域限制(因为它不是浏览器),但它可以作为中间层,通过添加 CORS 响应头或做代理来解决跨域。


4. Nginx 解决跨域的两种方式


方式一:添加 CORS 响应头(推荐)

场景:前端直接请求后端 API(后端已部署在 Nginx 上)

原理

添加以下 HTTP 响应头,使浏览器信任该请求的跨源访问:

Header 名称 说明
Access-Control-Allow-Origin 允许的跨域源(* 或具体域名)
Access-Control-Allow-Methods 允许的请求方法(如 GET,POST,PUT
Access-Control-Allow-Headers 允许的请求头(如 Authorization
Access-Control-Allow-Credentials 是否允许携带 Cookie(需具体域名,不能 *
Access-Control-Max-Age 预检请求缓存时间(秒)
Nginx 配置示例:
server {
    listen 80;
    server_name api.example.com;

    location /api/ {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
        add_header Access-Control-Allow-Headers Authorization,Content-Type;

        if ($request_method = OPTIONS ) {
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
            add_header Access-Control-Allow-Headers Authorization,Content-Type;
            add_header Content-Length 0;
            add_header Content-Type text/plain;
            return 204;
        }

        proxy_pass http://backend_server;
    }
}
注意事项:
  • 如果前端要带 cookie,Access-Control-Allow-Origin 不能是 *,必须指定具体域名,且加上:

add_header Access-Control-Allow-Credentials true;

方式二:通过 Nginx 反向代理解决跨域

场景:前端请求相同源的 Nginx,Nginx 再转发请求至后端不同源

这种方式从浏览器的角度看“没有跨域”,因为访问的地址始终是前端部署在同源的 Nginx。

原理

通过 Nginx 配置,将 /api/* 的请求转发到后端真实地址:

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

    location /api/ {
        proxy_pass http://backend.example.com/;
        proxy_set_header Host $host;
    }
}

前端请求示例:

axios.get("/api/user");
  • 前端以为在请求本域 /api/user(不触发 CORS)

  • 实际 Nginx 代理到 http://backend.example.com/user

✅ 这种方案彻底绕开浏览器同源限制,适用于内网 API 服务或多服务聚合。


5. 浏览器跨域判断原理总结

请求行为 浏览器做什么 服务端需要响应什么
简单请求 直接发请求 返回 Access-Control-Allow-Origin
非简单请求 先发预检(OPTIONS),再发正式请求 返回 Access-Control-Allow-Origin、Methods、Headers 等
withCredentials 跨域携带 Cookie 响应头中 Allow-Origin 不能是 * 且加 Allow-Credentials:true

6. 实践场景推荐方案

场景 推荐方式 是否支持 Cookie
前端直连后端 API(已配置 CORS) 添加 CORS 响应头
前端请求同域 Nginx,由其反代后端 反向代理
多服务统一网关接口聚合 网关反向代理统一出口
后端无法配置 CORS(第三方 API) 通过 Nginx 转发或服务中转

7. 常见错误与排查建议

错误描述 可能原因
No 'Access-Control-Allow-Origin' 未设置 CORS 响应头或设置不正确
OPTIONS 请求返回 404/405 后端或 Nginx 未正确处理预检请求
cookie 不生效 使用了 * 而非具体域名;未设置 withCredentials
Access-Control-Allow-Headers 缺失 设置了自定义头,但未在响应中声明允许

8. 补充:后端语言设置 CORS 的方式(参考)

后端语言 配置方式(示意)
Java (Spring Boot) @CrossOrigin 或 CORS Filter
Node.js (Express) cors 中间件
Python Flask flask-cors
PHP header('Access-Control-Allow-Origin: *')

9. 小结

项目 原理 Nginx 做法
跨域限制 同源策略,浏览器拦截非同源请求 添加 CORS 响应头 or 使用反向代理绕开
简单请求 直接发请求,校验响应头 添加 Access-Control-Allow-Origin
非简单请求 发起预检(OPTIONS)再请求 对 OPTIONS 请求添加响应并返回 204
带 Cookie 需指定域名 + Allow-Credentials: true Nginx 配置中同时设置域名和 credentials

四、Nginx令牌桶限流

令牌桶(Token Bucket)算法是一种用于 限流(Rate Limiting) 的核心算法,广泛用于:

  • 接口限流(如 Nginx、网关、API Server)

  • 网络通信(如路由器 QoS)

  • 服务熔断和拒绝策略(如 Sentinel、Envoy)

1. 令牌桶算法的核心思想

✅ 定义

令牌桶算法通过以固定速率往桶中放令牌(token),请求来临时从桶中取令牌,只有成功取到令牌的请求才被允许执行。


✅ 模型图解:

+------------------+       请求到达       +--------------------+
| 令牌定时器       |--------------------->| 尝试从桶中取一个令牌 |
| 每隔 T 毫秒放 1 个 |                    |   若取成功 => 通过    |
| 令牌到桶中       |                     |   否则 => 拒绝/等待  |
+------------------+                     +--------------------+

2. 核心参数

参数 含义
rate(速率) 每秒生成多少个令牌(如 10 token/sec)
capacity(桶大小) 桶中最多容纳多少令牌(避免 burst 时过载)
tokens 当前桶中实际令牌数量
lastRefillTime 上次放令牌的时间,用于计算应补充多少令牌

3. 请求流程(标准流程)

每当有一个请求到来:

  1. 刷新令牌数量:根据当前时间 - lastRefillTime 计算该放入多少令牌。

  2. 更新令牌数:桶中现有令牌数 = min(旧令牌 + 补充数, capacity)

  3. 检查令牌是否足够

    • 有令牌:取一个令牌,允许通过

    • 无令牌:请求被限流(丢弃/等待/排队等策略)


4. 代码实现(简化版本,Java 示例)

class TokenBucket {
    private final int capacity;
    private final int rate;
    private double tokens;
    private long lastRefillTimestamp;

    public TokenBucket(int capacity, int rate) {
        this.capacity = capacity;
        this.rate = rate;
        this.tokens = capacity;
        this.lastRefillTimestamp = System.currentTimeMillis();
    }

    public synchronized boolean allowRequest() {
        long now = System.currentTimeMillis();
        long deltaTime = now - lastRefillTimestamp;

        // 计算该生成多少新令牌
        double newTokens = deltaTime * rate / 1000.0;
        tokens = Math.min(capacity, tokens + newTokens);
        lastRefillTimestamp = now;

        if (tokens >= 1) {
            tokens -= 1;
            return true;
        } else {
            return false;
        }
    }
}

5. 与漏桶算法的对比

项目 令牌桶 Token Bucket 漏桶 Leaky Bucket
控流方式 请求需要令牌,按速率放令牌 请求入队,队列以固定速率“漏出”
允许突发请求 ✅(桶内有余令牌) ❌(请求被固定速率漏出)
限制速率 平均速率受限,最大速率受桶容量限制 严格平滑速率输出
典型用途 Nginx 限流、API 限流、Redis 哨兵 视频流输出、网络 QoS

6. 令牌桶的使用场景分析

1. Nginx 限流(使用漏桶 + 令牌桶思想)

limit_req_zone $binary_remote_addr zone=req_limit:10m rate=5r/s;

location /api/ {
    limit_req zone=req_limit burst=10 nodelay;
}
  • rate=5r/s:速率 = 5 个令牌/秒

  • burst=10:桶容量 = 10,允许突发 10 次

  • nodelay:有令牌立即放行(令牌桶特性)

本质是令牌桶思想:按速率补充令牌,有余量可突发请求。


2. 微服务 API 接口限流(如 Sentinel)

// 每秒最多允许 100 个请求
RateLimiter limiter = RateLimiter.create(100); // Guava 实现的令牌桶

if (limiter.tryAcquire()) {
    // 执行请求逻辑
} else {
    // 拒绝或降级
}

7. 常见扩展策略

1. 令牌时间过期控制

  • 可以为每个 token 附加 TTL,避免长时间积累失控

2. 按 IP/接口限流

  • key = client_ipkey = user_id + path,每个维度单独维护一个桶

3. 集群令牌桶

  • 分布式系统中,常结合 Redis 实现分布式令牌桶


8. 图解理解令牌桶

⏳ 时间轴演示(假设速率 5 r/s,容量 10)

t = 0s    tokens = 10 (满)
t = 0.2s  请求1来 => ✅
t = 0.4s  请求2来 => ✅
...
t = 1s    tokens += 5
t = 1.2s  请求10来 => ✅(用完 token)
t = 1.3s  请求11来 => ❌(没令牌)

9. 总结

关键点 说明
控制平均速率 固定速率补充令牌,控制长期请求平均速率
支持突发流量 桶内积累的令牌可以一次性消耗,支持短时间高并发
高性能易实现 时间窗口 + 数学补偿,无需复杂调度器
使用广泛 应用于网关、限流器、API 请求控制、DDOS 防护等

网站公告

今日签到

点亮在社区的每一天
去签到