Nginx 权威指南:从入门到精通

内容纲要

前言

1.1. 关于本手册

Nginx(发音为 "engine-x")是一款高性能的 HTTP 和反向代理服务器,也是一款 IMAP/POP3/SMTP 代理服务器。自其诞生以来,Nginx 因其稳定性、丰富的功能集、简单的配置文件和低系统资源消耗而广受欢迎。本手册旨在为从初学者到经验丰富的系统管理员和开发人员提供一份全面、深入且易于理解的 Nginx 指南。官方文档虽然详尽,但有时对于新手而言可能显得不够友好。本手册力求弥补这一差距,通过清晰的解释、实际的示例和最佳实践,帮助读者全面掌握 Nginx 的安装、配置、优化和安全加固。

1.2. 目标读者

本手册面向广泛的技术背景读者:

  • 初学者:希望了解 Nginx 的基本概念、安装和简单配置,以便快速搭建 Web 服务。
  • Web 开发人员:需要配置 Nginx 作为其应用程序(如 PHP、Python、Node.js 应用)的反向代理、处理静态资源或实现负载均衡。
  • 系统管理员:负责部署、管理和优化 Nginx 服务器,确保其高性能、高可用性和高安全性。
  • 高级工程师与架构师:寻求深入理解 Nginx 的高级特性、性能调优技巧和复杂场景下的解决方案。

无论您的经验水平如何,本手册都将提供有价值的信息和指导。

1.3. 手册结构

本手册系统地组织内容,循序渐进地引导读者掌握 Nginx 的各个方面:

  • 第一章:Nginx 简介 - 介绍 Nginx 的基本概念、核心特性、架构模型,并与 Apache 进行比较。
  • 第二章:Nginx 安装与初识 - 提供在 Ubuntu、CentOS、Docker 和 WSL 等主流平台上的详细安装步骤,以及从源码编译的方法。
  • 第三章:Nginx 配置核心 - 深入讲解 Nginx 配置文件的结构、语法、核心指令(如 serverlocationrootalias)及其工作原理。
  • 第四章:Nginx 作为反向代理 - 阐述如何配置 Nginx 作为反向代理,包括代理 HTTP/HTTPS 请求、WebSocket 连接以及 SSL 终止。
  • 第五章:Nginx 实现负载均衡 - 介绍 Nginx 的负载均衡功能,包括不同的负载均衡算法、服务器权重、健康检查和会话保持。
  • 第六章:Nginx 性能优化 - 探讨一系列性能调优技术,如优化 Worker 进程、连接处理、高效文件传输、内容缓存和数据压缩。
  • 第七章:Nginx 安全加固 - 涵盖 Nginx 的安全配置,包括 SSL/TLS 设置、HTTP Basic Authentication、IP 访问限制、速率限制和安全相关的 HTTP 头部。
  • 第八章:Nginx 故障排除 - 指导读者如何理解 Nginx 日志,诊断和解决常见的错误和问题。
  • 第九章:Nginx 模块 - 概述 Nginx 的模块化架构,介绍常用模块以及如何添加自定义模块。
  • 第十章:最佳实践与高级主题 - 分享 Nginx 配置和管理的最佳实践,以及一些高级应用技巧。
  • 第十一章:附录 - 提供常用的示例配置文件和进一步学习的资源链接。

通过学习本手册,读者将能够自信地在各种场景下部署和管理 Nginx 服务器。

第一章:Nginx 简介

1.1. Nginx 是什么?

Nginx 是一款开源的、高性能的 HTTP 和反向代理服务器,同时也被广泛用作邮件代理服务器(IMAP/POP3/SMTP)和通用的 TCP/UDP 代理服务器 1。它由 Igor Sysoev 于 2004 年首次发布,最初的目标是解决 C10k 问题,即如何处理一万个并发连接。由于其出色的性能、稳定性和低资源消耗,Nginx 迅速成为全球最受欢迎的 Web 服务器之一,被许多高流量网站采用,包括 Netflix、WordPress.com 和 Cloudflare。

Nginx 的设计重点在于高并发、高性能和低内存使用。它通过异步、事件驱动的架构来实现这些目标,这与传统的基于进程或线程的服务器(如 Apache 的早期模型)形成了鲜明对比 2。

1.2. Nginx 的核心特性

Nginx 提供了丰富的功能,使其能够胜任多种角色:

  • 静态内容服务:Nginx 在提供静态文件(如 HTML、CSS、JavaScript、图片)方面表现极为出色和高效 3。
  • 反向代理:作为反向代理,Nginx 可以将客户端请求转发到一个或多个后端应用服务器,并将响应返回给客户端。这有助于隐藏后端服务器的细节、实现负载均衡、SSL 终止等 3。
  • 负载均衡:Nginx 可以将传入的流量分发到多个后端服务器,以提高应用程序的可伸缩性和可靠性。它支持多种负载均衡算法 3。
  • SSL/TLS 终止:Nginx 可以处理传入的 HTTPS 连接,解密 SSL/TLS 流量,然后将未加密的请求转发到后端服务器。这减轻了后端应用服务器处理加密和解密的负担 3。
  • HTTP 缓存:Nginx 可以缓存来自后端服务器的响应,从而减少对后端服务器的请求,加快对客户端的响应速度 4。
  • URL 重写:通过 rewrite 模块,Nginx 能够根据复杂的规则修改请求 URI 3。
  • 访问控制:支持基于 IP 地址的访问控制和 HTTP Basic Authentication 9。
  • 速率限制:可以限制来自特定客户端 IP 地址的请求速率,以防止滥用和某些类型的 DoS 攻击 10。
  • WebSocket 支持:Nginx 可以代理 WebSocket 连接,这对于现代实时应用至关重要 3。
  • HTTP/2 和 HTTP/3 支持:Nginx 支持最新的 HTTP 协议版本,以提高 Web 性能。
  • 模块化架构:Nginx 的功能通过模块实现,用户可以根据需要选择和加载模块,甚至开发自定义模块 12。

1.3. Nginx 的架构:事件驱动与 Master-Worker 模型

Nginx 的高性能和高并发处理能力主要归功于其独特的架构设计,即事件驱动模型和 Master-Worker 进程模型 2。

1.3.1. 事件驱动架构

Nginx 采用非阻塞的、事件驱动的异步架构 2。与传统的为每个连接创建一个新进程或线程的服务器不同,Nginx 的单个 Worker 进程可以处理数千个并发连接。

  • 非阻塞 I/O:当 Nginx Worker 进程处理一个请求时,如果需要等待 I/O 操作(如从网络读取数据或向磁盘写入数据),它不会阻塞在那里。相反,它会注册对该事件的兴趣,然后继续处理其他请求。当 I/O 操作完成时,操作系统会通知 Nginx,相应的 Worker 进程再回过头来处理该请求 14。
  • 事件通知机制:Worker 进程使用高效的事件通知机制(如 Linux 上的 epoll 或 BSD 系统上的 kqueue)来监视多个套接字上的事件(如新连接、数据可读、数据可写)2。
  • 资源效率:这种架构极大地减少了上下文切换的开销和内存占用,因为不需要为每个连接维护单独的进程或线程堆栈。这使得 Nginx 即使在负载很高的情况下也能保持高性能和低资源消耗 2。

1.3.2. Master-Worker 进程模型

Nginx 采用 Master-Slave(主从)架构,具体表现为 Master 进程和多个 Worker 进程 2。

  • Master 进程
    • 主要职责是读取和评估配置文件,管理 Worker 进程 2。
    • 执行特权操作,如绑定端口、加载 SSL 证书。
    • 当 Nginx 启动时,Master 进程创建指定数量的 Worker 进程(通过 fork() 系统调用)14。
    • 监控 Worker 进程的健康状况,并在 Worker 进程意外终止时重新启动它们。
    • 处理控制信号,如平滑地重新加载配置 (reload)、平滑地关闭 (quit) 或立即停止 (stop) 15。
    • 在配置重载期间,Master 进程会启动新的 Worker 进程(使用新配置),并向旧的 Worker 进程发送信号,让它们在处理完当前所有请求后优雅地退出。这确保了服务的不间断升级 16。
  • Worker 进程
    • 实际处理客户端请求的进程 2。它们从 Master 进程继承监听套接字。
    • 每个 Worker 进程都是单线程的,并在其独立的事件循环中工作 2。
    • Worker 进程通过操作系统依赖的机制(如 accept_mutex 或套接字分片)有效地在它们之间分配新的客户端连接 15。
    • 通常建议将 Worker 进程的数量设置为等于服务器 CPU 核心的数量 (worker_processes auto;worker_processes <num_cores>;),以充分利用多核处理器的能力,并避免不必要的上下文切换 16。每个 Worker 进程可以被固定到一个 CPU 核心上,进一步减少上下文切换开销 2。
    • Worker 进程之间可以通过共享内存段共享一些数据,如缓存索引或会话持久性信息,但它们主要独立工作 14。
  • 缓存相关进程 (如果启用缓存)
    • Cache Loader 进程:在 Nginx 启动时运行,负责将磁盘上的缓存元数据加载到内存中,然后退出 15。
    • Cache Manager 进程:定期运行,负责清理磁盘缓存,确保缓存大小在配置的限制之内 2。

这种 Master-Worker 结合事件驱动的模型,使得 Nginx 能够高效地处理大量并发连接,同时保持较低的资源消耗和高度的稳定性 2。

1.4. Nginx 与 Apache 的比较

Nginx 和 Apache HTTP Server (简称 Apache) 是目前世界上使用最广泛的两种 Web 服务器软件。它们各有优缺点,适用于不同的场景。

1.4.1. 架构差异

  • Nginx:采用异步、事件驱动的架构。一个 Master 进程管理多个 Worker 进程,每个 Worker 进程可以高效地处理数千个并发连接,因为它们是非阻塞的,并且使用高效的事件通知机制(如 epollkqueue)2。
  • Apache:传统上采用基于进程或线程的架构。例如,Prefork MPM (Multi-Processing Module) 为每个请求创建一个进程,而 Worker MPM 或 Event MPM(较新的模型,更接近 Nginx 的事件驱动方式,但仍有差异)使用线程来处理多个请求。虽然 Apache 的 Event MPM 提高了并发处理能力,但其核心设计与 Nginx 仍有本质区别 3。

1.4.2. 性能与可伸缩性

  • Nginx:由于其事件驱动架构,Nginx 在处理高并发连接和静态内容方面通常表现出更高的性能和更好的可伸缩性,尤其是在高流量场景下。它的内存占用通常也更低 2。
  • Apache:在低到中等并发量下性能良好。对于动态内容的处理,Apache 可以通过将解释器(如 mod_php)直接嵌入其进程中来高效处理,但这可能导致更高的内存消耗。在高并发场景下,其基于进程/线程的模型可能会消耗更多资源,导致性能瓶颈 3。

1.4.3. 内容处理

  • Nginx:非常擅长处理静态内容。对于动态内容,Nginx 通常充当反向代理,将请求传递给后端的应用服务器(如 PHP-FPM、uWSGI、Node.js 服务器等)进行处理,然后将响应返回给客户端 3。Nginx 本身不直接执行动态语言脚本(除了通过 njs 模块支持部分 JavaScript)。
  • Apache:可以直接在服务器内部处理动态内容,通过诸如 mod_php, mod_perl, mod_python 等模块。这使得 Apache 在某些动态内容处理场景下配置更直接,但也可能导致更大的内存占用 3。

1.4.4. 模块系统与灵活性

  • Nginx:拥有一个核心模块集和许多第三方模块。模块通常在编译时静态链接到 Nginx 二进制文件中,尽管现在也支持动态模块加载。其模块系统虽然强大,但历史上不如 Apache 的模块生态系统庞大和灵活 12。
  • Apache:以其极其丰富的模块系统而闻名,提供了广泛的功能扩展。模块可以在运行时动态加载和卸载,这为 Apache 提供了极大的灵活性和可定制性 3。.htaccess 文件也为目录级配置提供了便利,但同时也可能带来性能开销。

1.4.5. 操作系统兼容性

  • Nginx:支持几乎所有的类 Unix 操作系统(如 Linux, macOS, BSD),但在 Windows 上的支持有限,性能也不如在类 Unix 系统上 3。
  • Apache:具有非常广泛的操作系统兼容性,包括各种类 Unix 系统、Windows 和 OpenVMS 等 12。

1.4.6. 何时选择 Nginx?何时选择 Apache?何时两者结合?

  • 选择 Nginx 的场景

    • 高并发、高流量的网站,尤其是静态内容密集型网站。
    • 作为反向代理和负载均衡器。
    • 需要低内存占用的环境。
    • SSL/TLS 终止。
  • 选择 Apache 的场景

    • 需要广泛的模块支持和高度灵活的配置。
    • 共享主机环境,其中 .htaccess 文件对于用户级配置非常重要。
    • 需要直接在 Web 服务器内部处理动态内容的传统应用。
  • 两者结合的场景:

    一种常见的强大组合是将 Nginx 作为前端反向代理,处理所有传入的客户端请求。Nginx 负责高效地提供静态内容、处理 SSL/TLS 连接、进行缓存和速率限制。然后,Nginx 将动态内容的请求代理到后端的 Apache 服务器(或其他应用服务器)进行处理 3。这种设置结合了 Nginx 在高并发和静态内容处理方面的优势,以及 Apache 在动态内容处理和模块灵活性方面的优势。例如,Nginx 在前台快速处理图片和 CSS,而 Apache 在后台处理需要 .htaccess 文件或特定 Apache 模块的动态逻辑 12。

下表总结了 Nginx 和 Apache 的一些关键区别:

特性 Nginx Apache
服务器架构 事件驱动,异步非阻塞 2 进程驱动/线程驱动 (Prefork, Worker, Event MPMs) 3
并发处理 非常高,资源消耗低 2 良好,但高并发下资源消耗可能较高 3
静态内容 性能极高 3 良好
动态内容 通常通过 FastCGI 等代理给后端处理 12 可通过模块直接在服务器内处理 (如 mod_php) 12
配置 语法简洁,但某些高级配置可能复杂 灵活,支持 .htaccess 目录级配置 3
模块系统 核心模块+第三方模块,支持动态加载 12 非常庞大和灵活的模块生态系统,支持动态加载 12
内存占用 通常较低 3 可能较高,取决于配置和模块 3
Windows 支持 有限,性能不如 Unix-like 12 良好 12

最终选择哪种服务器,或是否将它们结合使用,取决于具体的项目需求、预期的流量、可用的资源以及团队的技术栈。

第二章:Nginx 安装与初识

本章将指导您在几种主流操作系统和平台上安装 Nginx。我们将涵盖使用包管理器在 Ubuntu 和 CentOS 上安装、使用 Docker 部署以及在 Windows Subsystem for Linux (WSL) 中进行安装。此外,我们还将介绍从源代码编译安装 Nginx 的方法,这对于需要特定模块或最新版本的用户非常有用。

2.1. 在 Ubuntu 上安装 Nginx

在 Ubuntu 系统上安装 Nginx 通常非常直接,可以使用 apt 包管理器。

2.1.1. 使用 APT 包管理器安装

  1. 更新包列表:

    在安装任何新软件之前,建议首先更新本地的包索引,以确保获取到最新的软件包列表。

    Bash

    sudo apt update

    18

  2. 安装 Nginx:

    执行以下命令安装 Nginx:

    Bash

    sudo apt install nginx

    18

    apt 会处理所有必要的依赖项,并将 Nginx 安装到您的服务器上。

2.1.2. 防火墙配置

如果您的服务器启用了 ufw (Uncomplicated Firewall) 防火墙,您需要调整防火墙设置以允许外部访问 Nginx。Nginx 在安装时会向 ufw 注册几个应用配置文件 18。

  1. 查看可用的应用配置文件

    Bash

    sudo ufw app list

    您应该会看到类似以下的输出,其中包含 Nginx 的配置文件 18:

    Available applications:
     Nginx Full
     Nginx HTTP
     Nginx HTTPS
     OpenSSH
    • Nginx Full:开放端口 80 (正常的、未加密的 Web 流量) 和端口 443 (TLS/SSL 加密的流量)。
    • Nginx HTTP:仅开放端口 80。
    • Nginx HTTPS:仅开放端口 443。
  2. 允许 Nginx 流量:

    根据您的需求选择一个配置文件。如果您计划同时配置 HTTP 和 HTTPS,建议使用 Nginx Full:

    Bash

    sudo ufw allow 'Nginx Full'

    如果您只需要 HTTP,可以使用:

    Bash

    sudo ufw allow 'Nginx HTTP'
  3. 验证防火墙状态 (可选):

    Bash

    sudo ufw status

    您应该能看到相应的 Nginx 规则已启用。

2.1.3. 验证安装

安装完成后,Nginx 服务通常会自动启动。您可以通过几种方式验证 Nginx 是否正在运行:

  1. 检查服务状态

    Bash

    sudo systemctl status nginx

    如果 Nginx 正在运行,您会看到 active (running) 的状态信息 20。

  2. 通过浏览器访问:

    获取您服务器的公共 IP 地址(如果您在本地虚拟机上运行,则为该虚拟机的 IP 地址)。您可以使用命令 curl -4 icanhazip.com 来查找公共 IP 18。

    在 Web 浏览器中输入 http://YOUR_SERVER_IP。如果一切正常,您应该能看到 Nginx 的默认欢迎页面 18。

2.1.4. 管理 Nginx 进程

您可以使用 systemctl 命令来管理 Nginx 服务:

  • 停止 Nginx

    Bash

    sudo systemctl stop nginx
  • 启动 Nginx

    Bash

    sudo systemctl start nginx
  • 重启 Nginx

    (先停止再启动):

    Bash

    sudo systemctl restart nginx
  • 重新加载配置

    (平滑重载,不中断现有连接): 当您更改了 Nginx 配置文件后,应使用此命令使更改生效。

    Bash

    sudo systemctl reload nginx

    15

  • 禁用 Nginx 开机自启

    Bash

    sudo systemctl disable nginx

    18

  • 启用 Nginx 开机自启

    Bash

    sudo systemctl enable nginx

    18

在较旧的 Ubuntu 版本或不使用 systemd 的系统中,您可能会使用 service 命令,如 sudo service nginx start 19。

2.1.5. Ubuntu 默认目录结构

在 Ubuntu 系统上通过 apt 安装 Nginx 后,其主要的配置文件和目录通常位于:

  • 主配置文件/etc/nginx/nginx.conf 18。这是 Nginx 的全局配置文件。
  • 站点配置目录 (available)/etc/nginx/sites-available/ 18。这里存放每个站点的服务器块 (server block) 配置文件。在此目录中创建的文件不会立即被 Nginx 使用。
  • 站点配置目录 (enabled)/etc/nginx/sites-enabled/ 18。Nginx 在启动时会读取此目录中的配置文件。通常,这里的配置文件是指向 sites-available/ 目录下相应配置文件的符号链接。要启用一个站点,您需要在 sites-available/ 中创建配置文件,然后在此目录中为其创建一个符号链接。 例如:sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/ 18。
  • 配置片段目录/etc/nginx/snippets/ 18。此目录包含可以被包含到其他 Nginx 配置文件中的配置片段,非常适合存放可重复使用的配置段(如 SSL 参数、安全头部等)。
  • 主 Nginx 目录/etc/nginx/ 18。所有 Nginx 相关的配置文件都在这里。
  • 默认 Web 根目录/var/www/html/ 19。这是 Nginx 默认欢迎页面 index.html 存放的地方。您可以将其更改为您自己站点的根目录。
  • 日志文件目录/var/log/nginx/ 23。访问日志 (access.log) 和错误日志 (error.log) 存放在这里。

理解这些默认路径对于管理和配置 Nginx至关重要。

2.2. 在 CentOS 上安装 Nginx

在 CentOS (以及 RHEL、Rocky Linux、AlmaLinux 等衍生发行版) 上安装 Nginx 可以通过 yumdnf (较新版本) 包管理器。通常有两种主要方式:使用 EPEL (Extra Packages for Enterprise Linux) 仓库,或使用 Nginx 官方提供的仓库。

2.2.1. 使用 YUM/DNF 包管理器安装 (通过 EPEL)

EPEL 仓库提供了许多 CentOS 默认仓库中没有的额外软件包,包括 Nginx。

  1. 更新系统包 (可选但推荐):

    Bash

    sudo yum update  # 或者 sudo dnf update

    20

  2. 安装 EPEL 仓库

    Bash

    sudo yum install epel-release  # 或者 sudo dnf install epel-release

    20

    这将使 Nginx 包在您的系统中可用。

  3. 安装 Nginx

    Bash

    sudo yum install nginx  # 或者 sudo dnf install nginx

    20

    包管理器会自动安装 Nginx 及其依赖。

2.2.2. 从 Nginx 官方仓库安装

为了获取最新版本的 Nginx 或特定版本的 Nginx Plus,建议使用 Nginx 官方仓库。

  1. 创建 Nginx 仓库文件:

    创建一个名为 /etc/yum.repos.d/nginx.repo 的文件,并根据您的 CentOS 版本添加以下内容。

    对于 CentOS 7:

    Ini, TOML

    [nginx-stable]
    name=nginx stable repo
    baseurl=http://nginx.org/packages/centos/7/$basearch/
    gpgcheck=1
    enabled=1
    gpgkey=https://nginx.org/keys/nginx_signing.key
    module_hotfixes=true
    
    [nginx-mainline]
    name=nginx mainline repo
    baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
    gpgcheck=1
    enabled=0
    gpgkey=https://nginx.org/keys/nginx_signing.key
    module_hotfixes=true

    您可以将 enabled=1 设置为您希望默认安装的版本分支(stable 或 mainline)。mainline 分支包含最新的特性,而 stable 分支则更注重稳定性,只包含关键的 bug 修复。

    对于其他 CentOS 版本,请相应地替换 baseurl 中的版本号 (例如 centos/8/)。21 提供了一个通用的模板,但手动指定版本更为可靠。

  2. 安装 Nginx:

    如果您希望安装 stable 版本 (假设在 .repo 文件中 nginx-stable 的 enabled=1):

    Bash

    sudo yum install nginx  # 或者 sudo dnf install nginx

    如果您希望安装 mainline 版本,可以临时启用它:

    Bash

    sudo yum --enablerepo=nginx-mainline install nginx # 或者 sudo dnf --enablerepo=nginx-mainline install nginx

2.2.3. 防火墙配置

如果您的 CentOS 服务器启用了 firewalld,您需要允许 HTTP 和 HTTPS 流量。

Bash

sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload

20

这将永久地在公共区域开放 HTTP (端口 80) 和 HTTPS (端口 443) 服务,并重新加载防火墙规则使其生效。

2.2.4. 验证安装与管理进程

  1. 启动 Nginx 服务:

    与 Ubuntu 不同,通过 yum 或 dnf 安装的 Nginx 可能不会自动启动。

    Bash

    sudo systemctl start nginx

    20

  2. 设置 Nginx 开机自启

    Bash

    sudo systemctl enable nginx

    20

  3. 检查服务状态

    Bash

    sudo systemctl status nginx

    20

    您应该看到 active (running) 状态。

  4. 通过浏览器访问:

    获取服务器 IP 地址,并在浏览器中输入 http://YOUR_SERVER_IP。您应该能看到 Nginx 的默认欢迎页面。

  5. 管理 Nginx 进程:

    与 Ubuntu 类似,使用 systemctl 命令:

    • sudo systemctl stop nginx
    • sudo systemctl restart nginx
    • sudo systemctl reload nginx 20
    • sudo systemctl disable nginx

2.2.5. CentOS 默认目录结构

在 CentOS 系统上安装 Nginx 后,其主要的配置文件和目录通常位于:

  • 主配置文件/etc/nginx/nginx.conf 20。
  • 站点配置目录 (conf.d)/etc/nginx/conf.d/ 21。与 Ubuntu 的 sites-available/sites-enabled 结构不同,CentOS 上的 Nginx 通常将每个站点的服务器块配置文件(以 .conf 结尾)直接放在此目录中。nginx.conf 文件通常会包含一行 include /etc/nginx/conf.d/*.conf; 来加载这些站点配置。
  • 默认 Web 根目录/usr/share/nginx/html/ 22。Nginx 默认欢迎页面通常位于此。
  • 日志文件目录/var/log/nginx/ 25。访问日志 (access.log) 和错误日志 (error.log) 存放在这里。

如果您从 Nginx 官方仓库安装,默认的 nginx.conf 文件可能会有一个示例的 default.conf 文件在 /etc/nginx/conf.d/ 中,您可以修改或以此为模板创建自己的站点配置。

2.3. 使用 Docker 安装 Nginx

Docker 提供了一种轻量级、可移植的方式来部署 Nginx。Nginx 官方在 Docker Hub 上维护了官方镜像。

2.3.1. 拉取官方镜像

您可以从 Docker Hub 拉取最新的 Nginx 官方镜像:

Bash

docker pull nginx

或者,您可以指定一个特定的版本标签,例如 nginx:latestnginx:1.25.3。Nginx 官方还提供了基于不同操作系统的镜像标签,如 nginx:alpine (非常小巧的 Alpine Linux 版本) 或特定于 Nginx Plus 的标签 27。

2.3.2. 运行 Nginx 容器

最简单的运行 Nginx 容器的方式是:

Bash

docker run --name my-nginx-container -p 8080:80 -d nginx

28

解释:

  • docker run: 运行一个新容器。
  • --name my-nginx-container: 为容器指定一个名称。
  • -p 8080:80: 将主机的 8080 端口映射到容器的 80 端口。这意味着您可以通过访问主机的 http://localhost:8080 来访问 Nginx 服务。
  • -d: 以分离模式 (detached mode) 在后台运行容器。
  • nginx: 使用的镜像名称。

运行后,您可以在浏览器中访问 http://localhost:8080 (或您的 Docker 主机 IP) 来查看 Nginx 默认欢迎页面。

2.3.3. 挂载配置文件和静态内容

为了持久化配置和提供自定义内容,您通常需要将主机上的文件或目录挂载到容器中。

  1. 挂载自定义静态内容:

    假设您在主机上有一个目录 ~/my-site-content 包含您的 index.html 和其他静态文件。

    Bash

    mkdir -p ~/my-site-content
    echo "

    Hello from my custom Nginx container!

    " > ~/my-site-content/index.html

    然后运行容器,并将此目录挂载到容器内 Nginx 默认的 Web 根目录 /usr/share/nginx/html

    Bash

    docker run --name my-custom-nginx -p 8080:80 -d \
     -v ~/my-site-content:/usr/share/nginx/html:ro \
     nginx

    28

    这里 -v 参数用于创建卷挂载。~/my-site-content 是主机路径,/usr/share/nginx/html 是容器内路径。:ro 表示以只读方式挂载到容器,这是一个好习惯,除非容器需要写入该目录。

  2. 挂载自定义配置文件:

    假设您在主机上有一个自定义的 Nginx 配置文件 ~/my-nginx-config/nginx.conf (或者更常见的是,一个包含多个 .conf 文件的目录,如 ~/my-nginx-config/conf.d/)。

    您可以将主配置文件挂载到容器的 /etc/nginx/nginx.conf,或者将站点配置文件目录挂载到 /etc/nginx/conf.d/。

    例如,挂载一个自定义的 default.conf 到容器的 conf.d 目录:

    Bash

    mkdir -p ~/my-nginx-config/conf.d
    # 创建或复制您的 custom.conf 到 ~/my-nginx-config/conf.d/custom.conf
    # 例如,一个简单的 custom.conf:
    # server {
    #     listen 80;
    #     server_name localhost;
    #     location / {
    #         root /usr/share/nginx/html;
    #         index index.html index.htm;
    #     }
    #     location /custom {
    #         return 200 "This is a custom location!\n";
    #     }
    # }

    运行容器:

    Bash

    docker run --name my-configured-nginx -p 8080:80 -d \
     -v ~/my-site-content:/usr/share/nginx/html:ro \
     -v ~/my-nginx-config/conf.d:/etc/nginx/conf.d:ro \
     nginx

    Nginx 容器启动时会加载 /etc/nginx/conf.d/ 中的配置文件。

2.3.4. 构建自定义 Nginx 镜像

如果您需要将静态内容或配置文件直接打包到镜像中,而不是通过卷挂载,您可以创建一个 Dockerfile 来构建自定义镜像。

  1. 创建 Dockerfile:

    在您的项目目录(例如,与 my-site-content 和 my-nginx-config 同级或内部)创建一个名为 Dockerfile 的文件。

    示例 Dockerfile,将自定义 index.html 复制到镜像中:

    Dockerfile

    # 使用官方 Nginx 镜像作为基础镜像
    FROM nginx:latest
    
    # 删除默认的静态内容
    RUN rm /usr/share/nginx/html/*
    
    # 将主机上的 site-content 目录下的内容复制到容器的 Web 根目录
    COPY./my-site-content/ /usr/share/nginx/html/
    
    # (可选)复制自定义配置文件
    # COPY./my-nginx-config/conf.d/custom.conf /etc/nginx/conf.d/custom.conf
    
    # Nginx 默认会暴露 80 端口,这里可以显式声明
    EXPOSE 80
    
    # 基础镜像已经有 CMD ["nginx", "-g", "daemon off;"],通常不需要重写

    28

  2. 构建镜像:

    在包含 Dockerfile 的目录中运行:

    Bash

    docker build -t my-custom-nginx-image.

    28

    -t my-custom-nginx-image 为您的新镜像命名。

  3. 运行自定义镜像

    Bash

    docker run --name my-app-nginx -p 8080:80 -d my-custom-nginx-image

使用 Docker 部署 Nginx 具有环境一致性、易于扩展和管理的优点。

2.4. 在 Windows Subsystem for Linux (WSL) 上安装 Nginx

WSL 允许您在 Windows 上直接运行 Linux 环境,包括 Nginx。WSL 2 提供了更好的性能和完整的 Linux 内核兼容性,推荐使用。

2.4.1. 安装 WSL 和 Linux 发行版 (如 Ubuntu)

  1. 启用 WSL 功能:

    在 Windows PowerShell (以管理员身份运行) 中执行:

    PowerShell

    dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
    dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

    然后重启计算机。

  2. 安装 Linux 内核更新包 (对于 WSL 2):

    从 Microsoft 网站下载并安装最新的 WSL2 Linux 内核更新包 29。

  3. 设置 WSL 2 为默认版本

    PowerShell

    wsl --set-default-version 2

    29

  4. 从 Microsoft Store 安装 Linux 发行版:

    打开 Microsoft Store,搜索并安装您选择的 Linux 发行版,例如 "Ubuntu" 29。首次启动时,您需要设置用户名和密码。

或者,您可以使用 wsl --install 命令一步完成大部分设置 (需要较新版本的 Windows):

PowerShell

wsl --install

29

这会自动启用所需功能并安装默认的 Ubuntu 发行版。

2.4.2. 在 WSL 中安装 Nginx

一旦您的 Linux 发行版 (如 Ubuntu) 在 WSL 中运行,安装 Nginx 的过程与在原生 Linux 系统上基本相同。

  1. 打开 WSL 终端

  2. 更新包列表并安装 Nginx (以 Ubuntu 为例):

    Bash

    sudo apt update
    sudo apt install nginx -y

    29

  3. 启动 Nginx 服务:

    WSL (尤其是 WSL 1) 可能不完全支持 systemd。因此,您可能需要使用 service 命令来启动 Nginx:

    Bash

    sudo service nginx start

    29

    您可以使用 sudo service nginx status 来检查状态。

    对于 WSL 2,如果您的发行版支持 systemd (某些配置下可以启用),则 sudo systemctl start nginx 可能有效。

  4. 验证 Nginx:

    在 WSL 终端内,您可以使用 curl localhost 来测试 Nginx 是否响应。

2.4.3. WSL 网络配置与端口转发

从 Windows 主机或其他设备访问 WSL 中运行的 Nginx 需要一些网络配置。

  • WSL 2 网络:WSL 2 在其自己的虚拟化网络接口上运行,拥有独立的 IP 地址。

    • 从 Windows 主机访问:Windows 主机通常可以通过 localhost 或 WSL 2 虚拟机的 IP 地址访问 WSL 2 中监听的服务。例如,如果 Nginx 在 WSL 2 的 Ubuntu 中监听 80 端口,您可以直接在 Windows 浏览器中尝试 http://localhost

    • 获取 WSL IP 地址:在 WSL 终端中,使用 ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1hostname -I 来查找 WSL 实例的 IP 地址 29。

    • 从局域网其他设备访问

    :要使局域网中的其他设备能够访问 WSL 2 中的 Nginx,您需要在 Windows 主机上设置端口转发。

    1. 找到 WSL 的 IP 地址 (如上)。假设为 WSL_IP

    2. 在 Windows PowerShell (管理员) 中设置端口转发

      : 例如,将 Windows 主机的 80 端口转发到 WSL 的 80 端口:

      PowerShell

      netsh interface portproxy add v4tov4 listenport=80 listenaddress=0.0.0.0 connectport=80 connectaddress=WSL_IP

      30

    3. 配置 Windows 防火墙:添加入站规则以允许外部流量访问 Windows 主机上的转发端口 (例如,TCP 端口 80) 30。

  • Nginx 监听地址:

    默认情况下,Nginx 可能只监听 localhost (127.0.0.1)。为了能从 Windows 或其他网络设备访问,您可能需要修改 Nginx 配置 (例如 /etc/nginx/sites-enabled/default),将 listen 指令更改为监听所有接口或特定 IP:

    Nginx

    server {
      listen 80; # 或者 listen 0.0.0.0:80;
      #...
    }

    30

    修改后重启 Nginx:sudo service nginx restart。

2.4.4. WSL 特有的注意事项

  • Systemd 支持:WSL 1 不支持 systemd。WSL 2 对 systemd 的支持也可能需要额外配置 (例如使用 genie 或等待发行版的原生支持)。这意味着 systemctl 命令可能无法按预期工作,您可能需要依赖传统的 service 命令或直接运行 Nginx 可执行文件。
  • 性能:WSL 2 在文件系统性能方面(尤其是在 Windows 文件系统和 Linux 文件系统之间交互时)比 WSL 1 有显著改进。建议将项目文件放在 Linux 文件系统内 (例如,~/projects 而不是 /mnt/c/Users/...) 以获得最佳性能。
  • 资源消耗:WSL 2 会根据需要动态分配内存给 Linux 虚拟机,但有时可能不会及时释放。您可以通过在 Windows 用户目录下的 .wslconfig 文件来配置 WSL 2 的资源限制。

安装和配置 Nginx on WSL 对于在 Windows 环境下进行 Web 开发和测试非常方便。

2.5. 编译 Nginx 源码安装 (高级)

从源代码编译安装 Nginx 提供了最大的灵活性,允许您:

  • 使用最新版本的 Nginx (mainline 或 stable)。
  • 启用或禁用特定的模块。
  • 添加官方 Nginx 仓库中未包含的第三方模块。
  • 针对您的特定硬件进行优化。

2.5.1. 为何要从源码编译?

  • 自定义模块:最常见的原因是需要添加默认构建中不包含的模块,例如 Brotli 压缩模块 (如果发行版包未提供)、RTMP 模块、或特定的第三方认证模块 31。
  • 最新版本:官方包管理器中的 Nginx 版本可能不是最新的。
  • 性能调优:虽然较少见,但可以针对特定 CPU 架构进行编译优化。
  • 移除不需要的模块:可以创建一个更精简的 Nginx 构建。

2.5.2. 准备编译环境

编译 Nginx 需要一些开发工具和库:

  1. 安装构建工具:

    在 Ubuntu/Debian 上:

    Bash

    sudo apt update
    sudo apt install build-essential libpcre3-dev libssl-dev zlib1g-dev

    31

    在 CentOS/RHEL 上:

    Bash

    sudo yum groupinstall "Development Tools"
    sudo yum install pcre-devel openssl-devel zlib-devel

    这些包提供了:

    • build-essential / "Development Tools": C 编译器 (gcc), make 等。
    • libpcre3-dev / pcre-devel: Perl Compatible Regular Expressions 库,Nginx 的 rewrite 模块和 location 正则表达式匹配需要它。
    • libssl-dev / openssl-devel: OpenSSL 库,用于 HTTPS 支持。
    • zlib1g-dev / zlib-devel: zlib 库,用于 Gzip 压缩。
  2. 创建源码目录 (推荐):

    Bash

    sudo mkdir -p /usr/local/src/nginx
    sudo chown $(whoami):$(whoami) /usr/local/src/nginx # 赋予当前用户权限
    cd /usr/local/src/nginx

    32

2.5.3. 下载源码与校验

  1. 下载 Nginx 源码:

    访问 Nginx 官方网站 (nginx.org) 下载最新的 stable 或 mainline 版本的源码包 (.tar.gz)。

    或者使用 wget:

    Bash

    # 替换为实际的最新版本号
    wget http://nginx.org/download/nginx-1.25.3.tar.gz

    31

  2. (可选但推荐) 校验源码完整性:

    Nginx 官方会提供源码包的 PGP 签名 (.asc 文件)。您可以下载签名文件,并导入 Nginx 开发者 (如 Maxim Dounin) 的公钥来验证下载的源码包是否真实且未被篡改 32。

    Bash

    wget http://nginx.org/download/nginx-1.25.3.tar.gz.asc
    wget http://nginx.org/keys/mdounin.key
    gpg --import mdounin.key
    gpg --verify nginx-1.25.3.tar.gz.asc nginx-1.25.3.tar.gz

    如果签名有效,您会看到 "Good signature" 的消息。

  3. 解压源码

    Bash

    tar -zxvf nginx-1.25.3.tar.gz
    cd nginx-1.25.3

    31

2.5.4. 配置编译选项 (./configure)

./configure 脚本用于检查系统环境、设置编译参数和指定要包含的模块。

  1. 查看当前 Nginx 的编译选项 (如果已通过包管理器安装了一个版本):

    运行 nginx -V 命令可以显示当前 Nginx 是如何编译的,这对于确保新编译的版本包含所有必要的功能非常有用 33。

    Bash

    nginx -V

    输出会包含类似 --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx 等选项。

  2. 运行 ./configure:

    一个典型的配置命令可能如下:

./configure

--prefix=/etc/nginx

--sbin-path=/usr/sbin/nginx

--modules-path=/usr/lib/nginx/modules

--conf-path=/etc/nginx/nginx.conf

--error-log-path=/var/log/nginx/error.log

--http-log-path=/var/log/nginx/access.log

--pid-path=/var/run/nginx.pid

--lock-path=/var/run/nginx.lock

--http-client-body-temp-path=/var/cache/nginx/client_temp

--http-proxy-temp-path=/var/cache/nginx/proxy_temp

--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp

--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp

--http-scgi-temp-path=/var/cache/nginx/scgi_temp

--user=nginx

--group=nginx

--with-compat

--with-file-aio

--with-threads

--with-http_addition_module

--with-http_auth_request_module

--with-http_dav_module

--with-http_flv_module

--with-http_gunzip_module

--with-http_gzip_static_module

--with-http_mp4_module

--with-http_random_index_module

--with-http_realip_module

--with-http_secure_link_module

--with-http_slice_module

--with-http_ssl_module

--with-http_stub_status_module

--with-http_sub_module

--with-http_v2_module

--with-mail

--with-mail_ssl_module

--with-stream

--with-stream_realip_module

--with-stream_ssl_module

--with-stream_ssl_preread_module

--add-module=/path/to/external-module-source # 添加静态第三方模块 31

# --add-dynamic-module=/path/to/external-module-source # 添加动态第三方模块

```

31

重要:

* 上述选项是一个示例,您应该根据 nginx -V 的输出和您的需求进行调整。

* 路径 (--prefix, --conf-path 等) 应与您的系统标准或期望一致。

* --with-http_ssl_module 是启用 HTTPS 的关键。

* --add-module=/path/to/module_source 用于编译静态模块。模块的源码需要预先下载或克隆到指定路径。

* --add-dynamic-module=/path/to/module_source 用于编译动态模块,编译后会生成 .so 文件。

运行 `./configure` 后,它会生成 `Makefile`。仔细检查输出,确保没有错误,并且所有期望的模块都已找到。

2.5.5. 编译与安装 (make && make install)

  1. 编译 Nginx

    Bash

    make

    31

    这将根据 Makefile 编译 Nginx。

  2. 安装 Nginx

    Bash

    sudo make install

    31

    这会将编译好的 Nginx 二进制文件、模块和默认配置文件安装到您在 ./configure 中指定的路径。

    注意:如果系统中已存在 Nginx,make install 可能会覆盖现有文件。在生产环境中操作时务必小心。建议先备份现有配置 (sudo cp -r /etc/nginx ~/nginx-backup) 31。或者,在 ./configure 时使用不同的 --prefix 将其安装到自定义位置进行测试。

2.5.6. 创建 systemd 服务文件

如果您的系统使用 systemd,并且您将 Nginx 安装到了非标准路径,或者您希望自定义服务管理,您可能需要创建或修改 Nginx 的 systemd 服务文件。

通常,服务文件位于 /lib/systemd/system/nginx.service 或 /etc/systemd/system/nginx.service。

一个示例 nginx.service 文件可能如下 31:

Ini, TOML

[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network.target nss-lookup.target

Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /var/run/nginx.pid
TimeoutStopSec=5
KillMode=mixed
PrivateTmp=true

[Install]
WantedBy=multi-user.target

确保 ExecStart, PIDFile 等路径与您的编译安装路径一致。

创建或修改后,重新加载 systemd 配置并启用服务:

Bash

sudo systemctl daemon-reload
sudo systemctl enable nginx
sudo systemctl start nginx

编译安装 Nginx 提供了高度的控制权,但也需要更仔细的依赖管理和维护。

第三章:Nginx 配置核心

理解 Nginx 的配置文件结构和核心指令是有效使用 Nginx 的关键。Nginx 的配置以其简洁和强大的表达能力而著称,但也需要精确的语法。

3.1. 配置文件结构与语法基础

Nginx 的配置由一系列指令 (directives)块 (blocks 或 contexts) 组成 25。

  • 指令 (Directives):指令是 Nginx 配置的基本单元,通常由一个名称和一个或多个参数组成,以分号 (;) 结尾 25。例如:worker_processes auto;listen 80;
  • 块 (Blocks/Contexts):块是将相关指令组织在一起的容器,使用花括号 ({}) 定义。常见的块有 events {}, http {}, server {}, location {} 等 25。块可以嵌套,例如 server 块位于 http 块内部,而 location 块位于 server 块内部。
  • 注释 (Comments):以 # 开头的行被视为注释,Nginx 会忽略它们 25。
  • 继承模型:指令通常从父块继承到子块。例如,在 http 块中定义的指令会应用于所有内部的 server 块,除非在 server 块中被明确覆盖。同样,server 块中的指令会应用于其内部的 location 块,除非被覆盖。理解这种继承关系对于避免配置冗余和意外行为非常重要。

Nginx 的主配置文件通常是 /etc/nginx/nginx.conf 18。为了保持配置的模块化和可管理性,这个主文件通常会使用 include 指令来加载其他目录中的配置文件,如 /etc/nginx/conf.d/*.conf/etc/nginx/sites-enabled/* 18。

3.2. main 上下文

main (或称为全局) 上下文是指位于任何特定块之外的指令。这些指令在 nginx.conf 文件的顶层定义,影响 Nginx 的整体行为 25。

常见的 main 上下文指令包括:

  • user <user> [group];:指定 Nginx Worker 进程运行时使用的用户和组。例如 user www-data; 23。出于安全考虑,通常不应使用 root 用户。
  • worker_processes <number | auto>;:设置 Nginx Worker 进程的数量。auto 会尝试自动检测 CPU核心数并以此为准 15。详细讨论见性能优化章节。
  • error_log <file> [level];:配置错误日志文件的路径和记录级别。例如 error_log /var/log/nginx/error.log warn; 23。日志级别包括 debug, info, notice, warn, error, crit, alert, emerg
  • pid <file>;:指定存储 Master 进程 PID (进程ID) 的文件路径。例如 pid /var/run/nginx.pid; 23。
  • load_module <modules/module_name.so>;:用于加载动态编译的模块 13。例如 load_module modules/ngx_http_brotli_filter_module.so;

3.3. events 上下文

events 块包含影响 Nginx 连接处理的全局指令 23。每个配置文件中只能有一个 events 块。

核心指令是:

  • worker_connections ;

    :设置每个 Worker 进程可以同时处理的最大连接数

    17

    。这个数字包括所有连接(例如与客户端的连接、与后端代理服务器的连接等),而不仅仅是客户端连接。默认值通常是 512 或 1024。

    • 服务器可以处理的总最大客户端连接数近似为 worker_processes * worker_connections (除以2或4,如果Nginx同时作为反向代理,因为每个客户端请求可能需要一个到客户端的连接和一个到后端的连接)。
    • 此值受限于系统的文件描述符限制 (RLIMIT_NOFILE)。
  • multi_accept <on | off>;:如果启用 (on),Worker 进程会尝试一次性接受所有等待中的新连接。如果禁用 (off,默认),Worker 进程一次只接受一个新连接。在某些情况下,启用此选项可以提高新连接的处理效率。

  • use <method>;:指定 Nginx 使用的连接处理方法,如 epoll (Linux), kqueue (BSD), eventport (Solaris)。Nginx 通常会自动选择系统支持的最优方法。

示例:

Nginx

events {
    worker_connections 768;
    # multi_accept on;
}

3.4. http 上下文

http 块是配置 HTTP/HTTPS 服务的主要区域。它包含了定义 HTTP 服务器行为的所有指令和 server 块 23。

http 块中常见的指令包括:

  • include /etc/nginx/mime.types;:包含一个文件,该文件将文件扩展名映射到 MIME 类型,以便 Nginx 可以为客户端提供正确的 Content-Type 头部 23。

  • default_type application/octet-stream;:如果无法从 mime.types 文件中确定文件类型,则使用此默认 MIME 类型 23。

  • log_format  '';

    :定义访问日志的格式。Nginx 预定义了一个名为

    combined

    的格式,但您可以创建自定义格式。例如:

    Nginx

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    23

  • access_log <path> [format [buffer=size][gzip[=level]][flush=time][if=condition]];:设置访问日志文件的路径和使用的格式。例如 access_log /var/log/nginx/access.log main; 23。

  • sendfile on;:启用或禁用 sendfile() 系统调用来传输文件。详细讨论见性能优化章节 4。

  • tcp_nopush on;:仅在 sendfile on; 时生效,尝试在单个 TCP 包中发送 HTTP 响应头和文件开头。详细讨论见性能优化章节 26。

  • tcp_nodelay on;:启用或禁用 TCP_NODELAY 选项。详细讨论见性能优化章节 26。

  • keepalive_timeout <timeout>;:设置与客户端的长连接超时时间。例如 keepalive_timeout 65; 17。

  • keepalive_requests <number>;:设置通过一个长连接可以服务的最大请求数 17。

  • client_max_body_size <size>;:限制客户端请求体的最大允许大小。例如 client_max_body_size 8m; 36。这对于防止恶意的大文件上传非常重要。

  • server_tokens <on | off | build | string>;:控制 Nginx 是否在错误页面和 "Server" 响应头中显示其版本号。出于安全考虑,通常建议设置为 off

  • include /etc/nginx/conf.d/*.conf;:加载 conf.d 目录下的所有 .conf 文件,通常用于存放全局 HTTP 配置或第三方模块的配置。

  • include /etc/nginx/sites-enabled/*;:(常见于 Debian/Ubuntu) 加载 sites-enabled 目录下的所有站点配置文件。

http 块内可以包含一个或多个 server 块,每个 server 块定义一个虚拟主机。

3.5. Server 块 (server): 虚拟主机

server 块用于定义一个虚拟主机,使其能够处理特定域名、IP 地址和端口的请求 37。通过配置多个 server 块,Nginx 可以在单个物理服务器和 IP 地址上托管多个网站。

每个 server 块通常包含 listenserver_name 指令,以及一个或多个 location 块来处理不同的 URI。

3.5.1. listen 指令

listen 指令指定 Nginx 监听的 IP 地址和端口组合 38。

  • 语法:listen address[:port][default_server][ssl][http2][proxy_protocol]...;

    listen port [default_server][ssl][http2][proxy_protocol]...;

    listen unix:path [default_server][ssl][http2][proxy_protocol]...;

  • 常见形式

    • listen 127.0.0.1:8080;:监听特定 IP 地址和端口 38。
    • listen 80;:监听所有可用网络接口的 80 端口。
    • listen [::]:80;:监听所有可用 IPv6 网络接口的 80 端口 19。
    • listen 443 ssl;:监听 443 端口并启用 SSL/TLS 处理 5。ssl 参数是必需的,以便 Nginx 知道这是一个 HTTPS 服务器。
    • listen unix:/var/run/nginx.sock;:监听 Unix 套接字。
  • 默认值 37:

    • 如果 server 块中没有 listen 指令,则默认为 listen *:80; (如果 Nginx 以 root 用户启动) 或 listen *:8000; (如果以非 root 用户启动且没有权限绑定到低端口)。更安全的说法是,如果没有 listen 指令,Nginx 会尝试使用 0.0.0.0:80
    • 如果只指定 IP 地址 (如 listen 192.168.1.1;),则默认端口为 80。
    • 如果只指定端口 (如 listen 8888;),则默认监听所有 IP 地址的该端口 (0.0.0.0:8888)。
  • default_server 参数:

    对于每个 IP:Port 组合,可以有一个 server 块被标记为 default_server 25。如果一个请求到达该 IP:Port,但其 "Host" 头部不匹配任何已配置的 server_name,则该请求将由 default_server 处理。如果没有显式指定 default_server,则该 IP:Port 上的第一个 server 块将成为隐式的默认服务器。

    这对于捕获未明确配置的域名请求或直接通过 IP 地址访问的请求非常有用,通常可以配置为返回一个错误页面 (如 404) 或一个通用的占位页面。

    Nginx

    server {
      listen 80 default_server;
      listen [::]:80 default_server;
      server_name _; # 通常与 default_server 一起使用
      return 404; # 或者 serve 一个默认页面
    }

3.5.2. server_name 指令

server_name 指令定义了此 server 块负责处理哪些域名(主机名)的请求 37。Nginx 会将客户端请求中的 "Host" HTTP 头部与各个 server 块的 server_name 进行匹配。

  • 语法:server_name name...;

    可以列出一个或多个名称,用空格分隔。

  • 名称类型

    1. 精确名称:例如 server_name example.com www.example.com; 25。

    2. 通配符名称

      :星号 (

      *

      ) 只能出现在名称的开头或结尾,并且只能在点 (

      .

      ) 的边界。

      • server_name *.example.com; (匹配 www.example.com, api.example.com 但不匹配 example.com) 38。
      • server_name www.*; (匹配 www.example.com, www.example.org) 38。
      • server_name.example.com; (一个特殊的通配符,匹配 example.com*.example.com) 43。
    3. 正则表达式名称

      :以波浪号 (

      ~

      ) 开头。正则表达式遵循 PCRE (Perl Compatible Regular Expressions) 语法。

      • server_name ~^www\d+\.example\.net$; (匹配 www01.example.net, www123.example.net 等) 38。
      • 正则表达式匹配是按顺序的,第一个匹配的正则表达式将被使用。
      • 如果正则表达式包含花括号 ({, }),则整个表达式需要用引号括起来,例如 server_name "~^(?<subdomain>\w+)\.example\.com$";
    4. 特殊名称

      • _ (下划线):一个无效的域名,通常用作 default_serverserver_name,以捕获所有不匹配其他 server_name 的请求 40。
      • "" (空字符串):匹配没有 "Host" HTTP 头部的请求 43。
      • IP 地址:可以将 IP 地址用作 server_name 来处理直接通过 IP 访问的请求 42。
  • 匹配顺序 38:

    当一个请求到达 Nginx,并且其 IP 地址和端口匹配多个 server 块时,Nginx 会根据以下优先级选择 server_name:

    1. 精确名称
    2. 最长的以星号开头的通配符名称 (例如 *.example.com 优先于 *.com)。
    3. 最长的以星号结尾的通配符名称 (例如 www.* 优先于 w.*)。
    4. 第一个匹配的正则表达式名称 (按配置文件中出现的顺序)。 如果没有找到匹配的 server_name,则请求将由该 IP:Port 组合的 default_server 处理。

Nginx 的服务器块选择逻辑是一个两阶段过程:首先,它根据请求的 IP 地址和端口,使用 listen 指令来确定一个最具体的匹配列表。如果这个列表中有多个 server 块具有相同的特异性(例如,都是 listen 80;),Nginx 才会接着评估这些块的 server_name 指令与请求的 "Host" 头部进行匹配 37。例如,如果同时存在 listen 192.168.1.100:80;listen 0.0.0.0:80; (即 listen 80;),一个发往 192.168.1.100:80 的请求会优先考虑前者进行 server_name 匹配。只有在 IP:Port 匹配特异性相同的情况下,server_name 才成为决定因素。default_server 是针对特定 IP:Port 组合的最终回退机制。

3.6. Location 块 (location): 处理 URI

location 块定义了 Nginx 如何处理不同的请求 URI (Uniform Resource Identifier),即 URL 中主机名和端口之后的部分 37。location 块位于 server 块内部,也可以嵌套在其他 location 块中。

3.6.1. URI 匹配模式与修饰符

location 指令后面可以跟一个可选的修饰符和一个匹配模式 (通常是前缀字符串或正则表达式) 25。

修饰符 描述 示例 优先级说明
(无) 前缀匹配 (不区分大小写,除非文件系统本身区分)。匹配以指定前缀开头的 URI。 location /path/ {... } 参与“最长前缀”选择。
= 精确匹配。请求 URI 必须与指定字符串完全相同。 location = /exact_match {... } 最高优先级。如果匹配,搜索立即停止,并使用此 location。
~ 正则表达式匹配 (区分大小写)。 `location ~ .(gif\ jpg\
~* 正则表达式匹配 (不区分大小写)。 location ~* .(png\ | css)$ {... }
^~ “非正则表达式”最长前缀匹配。如果此最长前缀匹配成功,则不再检查正则表达式。 location ^~ /images/ {... } 如果此 location 是最长的前缀匹配,则 Nginx 会使用它,并且不会再进行后续的正则表达式匹配。这对于优先处理某些前缀非常有用。

理解这些修饰符及其行为对于正确路由请求至关重要。例如,location / 会匹配所有请求,因为它是一切 URI 的前缀,但通常它的优先级较低,除非没有其他更具体的匹配。

3.6.2. Location 选择算法

Nginx 使用以下算法来选择处理请求的 location 块 37:

  1. 检查精确匹配 (=):Nginx 首先查找使用 = 修饰符且与请求 URI 完全匹配的 location。如果找到,则立即选择此 location 并停止搜索。

  2. 检查所有前缀匹配:如果没有找到精确匹配,Nginx 会检查所有非正则表达式的 location (即没有修饰符或使用 ^~ 修饰符的)。

  3. 确定最长的前缀匹配

    :在所有前缀匹配中,Nginx 找出与请求 URI 匹配的最长的前缀。

    • 如果这个最长的前缀匹配使用了 ^~ 修饰符,则 Nginx 立即选择此 location,并且不再检查任何正则表达式 location
    • 如果这个最长的前缀匹配没有使用 ^~ 修饰符,Nginx 会暂时存储这个匹配。
  4. 评估正则表达式匹配:如果搜索没有因 =^~ (在最长前缀上) 而停止,Nginx 现在会按它们在配置文件中出现的顺序来评估所有正则表达式 location (~~*)。

  5. 选择第一个匹配的正则表达式:一旦找到第一个与请求 URI 匹配的正则表达式 location,Nginx 就会选择它并停止搜索。

  6. 回退到存储的前缀匹配:如果在步骤 4 和 5 中没有找到任何匹配的正则表达式 location,则 Nginx 会选择在步骤 3 中存储的那个最长的前缀匹配。

一个常见的混淆点是 location 块的顺序。对于正则表达式 location,它们在配置文件中的顺序非常重要,因为 Nginx 会使用第一个匹配到的。然而,对于前缀 location,它们的顺序不影响选择逻辑,Nginx 总是会先找到“最长的”匹配,然后再决定是否因 ^~ 而停止或继续检查正则表达式。精确匹配 (=) 或在最长前缀上的 ^~ 会阻止正则表达式的检查,这可能导致意外行为,如果开发者不清楚这个优先级。

3.6.3. rootalias 指令

rootalias 指令用于指定 location 块所服务的文件的文件系统路径。它们都用于服务静态文件,但工作方式有所不同。

  • root 指令 26:

    • 语法:root /path/to/directory;

    • 行为:root 指令设置请求的根目录。Nginx 会将请求 URI (或者 location 匹配后的剩余部分) 追加root 指令指定的路径后面,以构成完整的文件系统路径。

    • root 可以在 http, server, 或 location 上下文中使用。如果在 location 中指定,它会覆盖从父块继承的值。

    • 示例:

    Nginx

    server {
        root /var/www/html; # 默认根目录
    
        location /static/ {
            # 此处未指定 root,继承 /var/www/html
            # 请求 /static/image.png 会查找 /var/www/html/static/image.png
        }
    
        location /app/ {
            root /srv/myapp; # 为 /app/ 及其子路径指定新的根目录
            # 请求 /app/script.js 会查找 /srv/myapp/app/script.js
        }
    }
  • alias 指令 45:

    • 语法:alias /path/to/directory/;

    • 行为:alias 指令用指定的路径替换 location 匹配的 URI 部分。它仅能在 location 上下文中使用。

    • 示例:

    Nginx

    location /media/ {
        alias /var/www/shared_media/;
        # 请求 /media/video.mp4 会查找 /var/www/shared_media/video.mp4
        # 注意:/media/ 部分被 /var/www/shared_media/ 替换了
    }
    
    location /downloads/specific_file.zip {
        alias /opt/archive/latest_release.zip;
        # 请求 /downloads/specific_file.zip 会直接查找 /opt/archive/latest_release.zip
    }
  • rootalias 的关键区别 45:

    • root: 将 URI 附加到根路径。
    • alias: 将 location 匹配的 URI 部分替换为别名路径。

    使用 alias 时,路径末尾的斜杠 (/) 非常重要。一个常见的做法是:如果 location 路径以 / 结尾,则 alias 路径也应以 / 结尾,反之亦然,以避免路径拼接错误。例如:

    • location /prefix/ { alias /real/path/; } (推荐) - 请求 /prefix/file 映射到 /real/path/file
    • location /prefix { alias /real/path; } (如果 /prefix 不代表目录,也可以) - 请求 /prefix/file 映射到 /real/path/file
    • 如果 location /prefix/ { alias /real/path; } (不一致的斜杠) - 请求 /prefix/file 可能映射到 /real/pathfile (错误)。 虽然 45 没有深入探讨斜杠问题,但这是使用 alias 时的常见陷阱,一个专业的指南应当提及。

3.7. 静态内容服务

Nginx 以其高效服务静态内容的能力而闻名 1。这通常通过结合 location 块、rootalias 指令以及其他相关指令来实现。

  • index 指令

    • 语法:index file...;
    • 行为:当请求的 URI 指向一个目录时,index 指令定义了 Nginx 在该目录中查找的默认文件(索引文件)的名称和顺序 4。Nginx 会按列出的顺序查找这些文件,并提供第一个找到的文件。
    • 示例:index index.html index.htm default.html;
    • 如果找不到任何指定的索引文件,Nginx 默认会返回 403 Forbidden 错误(如果 autoindex off;)或 404 Not Found(取决于具体配置和文件系统情况)。
  • try_files 指令

    • 语法:try_files file... uri;try_files file... =code;
    • 行为:按顺序检查指定文件或目录是否存在。它会使用第一个找到的文件或目录来处理请求。如果所有列出的文件/目录都不存在,则会进行内部重定向到指令的最后一个参数指定的 URI,或者返回指定的 HTTP 状态码 4。
    • 变量 $uri 代表当前请求的规范化 URI,$uri/ 代表检查 URI 是否为一个目录。
    • 示例:
    • try_files $uri $uri/ /index.html;:首先尝试按字面 URI 查找文件,如果找不到,则尝试将其作为目录查找(并寻找 index 文件),如果还找不到,则内部重定向到 /index.html。这常用于单页应用 (SPA)。
    • try_files $uri $uri/ =404;:如果文件或目录不存在,则返回 404 错误。
  • autoindex on; 指令

    • 语法:autoindex on | off;
    • 行为:如果设置为 on,并且请求的 URI 是一个目录,且该目录中没有找到 index 指令指定的任何索引文件,Nginx 将生成并显示该目录的文件列表 26。
    • 默认值为 off。出于安全考虑,通常不建议在面向公众的目录上启用 autoindex
  • 示例配置 (服务静态站点)

    Nginx

    server {
      listen 80;
      server_name example.com;
      root /var/www/example.com/public; # 站点文件根目录
      index index.html index.htm;       # 默认索引文件
    
      location / {
          try_files $uri $uri/ /index.html =404; # 尝试文件,然后目录,然后回退到 index.html 或 404
      }
    
      location /images/ {
          # 此 location 块可以为 /images/ 目录下的文件设置特定规则,
          # 例如缓存头、访问日志关闭等。
          # 如果没有指定 root,它会继承 server 块的 root。
          # 所以 /images/pic.jpg 会在 /var/www/example.com/public/images/pic.jpg 查找。
          expires 7d; # 设置浏览器缓存7天
          access_log off; # 关闭此 location 的访问日志
      }
    
      location ~* \.(css|js)$ {
          # 为所有 CSS 和 JS 文件设置规则
          expires 1y; # 缓存1年
          add_header Cache-Control "public";
      }
    }

3.8. 理解 Nginx 变量

Nginx 提供了大量预定义的内置变量,这些变量可以在配置文件中用于各种目的,如日志记录、条件逻辑 (通过 if 指令,但应谨慎使用)、URL 重写、设置头部信息等 26。这些变量的值是在请求处理时动态确定的。

一些常用的 Nginx 变量包括:

  • $args:请求中的参数字符串 (查询字符串)。
  • $binary_remote_addr:客户端地址的二进制形式,用于 limit_req_zone 等。
  • $body_bytes_sent:发送给客户端的响应体字节数,不包括响应头。
  • $content_length:请求头 "Content-Length" 字段的值。
  • $content_type:请求头 "Content-Type" 字段的值。
  • $document_root:当前请求的根目录路径,由 rootalias 指令设置。
  • $host:按以下优先级顺序:请求行中的主机名,或 "Host" 请求头字段中的主机名,或与请求匹配的 server_name
  • $http_<header_name>:任意 HTTP 请求头的值,例如 $http_user_agent (User-Agent 头),$http_referer (Referer 头)。头部名称中的连字符 (-) 会被替换为下划线 (_),且名称会转换为小写。
  • $httpsis_args:如果 $args 不为空,则为 "?",否则为空字符串。
  • $limit_rate:允许向客户端传输响应的速率限制。
  • $msec:当前时间的毫秒数 (用于日志记录或生成唯一 ID)。
  • $nginx_version:Nginx 版本号。
  • $pid:Worker 进程的 PID。
  • $proxy_add_x_forwarded_for:包含 $remote_addr 和现有 "X-Forwarded-For" 请求头的值,用逗号分隔。
  • $query_string:同 $args
  • $remote_addr:客户端的 IP 地址。
  • $remote_port:客户端的端口号。
  • $remote_user:由 HTTP Basic Authentication 提供的用户名。
  • $request:完整的原始请求行,例如 "GET /index.html HTTP/1.1"。
  • $request_body:客户端请求体。此变量的值仅在请求体被读取到内存缓冲区后才可用。
  • $request_completion:如果请求已完成,则为 "OK",否则为空字符串。
  • $request_filename:当前请求所映射到的文件系统路径,基于 rootalias 指令以及 URI。
  • $request_method:请求方法,如 "GET", "POST"。
  • $request_time:请求处理时间,从读取客户端第一个字节到发送最后一个字节给客户端的时间,单位为秒,精度为毫秒。
  • $request_uri:完整的原始请求 URI (带参数),例如 "/foo/bar.php?arg=value"。
  • $scheme:请求方案,"http" 或 "https"。
  • $server_addr:接受请求的服务器的 IP 地址。
  • $server_name:处理当前请求的 server_name
  • $server_port:接受请求的服务器的端口号。
  • $server_protocol:请求协议,通常为 "HTTP/1.0", "HTTP/1.1", 或 "HTTP/2"。
  • $status:响应状态码,例如 200, 404, 502。
  • $uri:当前请求的规范化 URI (不带参数,经过解码、相对路径解析和可能的尾部斜杠添加/移除)。例如,对于 "/foo/bar.php?arg=value",$uri 是 "/foo/bar.php"。
  • $upstream_response_time:从上游服务器接收响应所花费的时间。

除了这些内置变量,用户还可以使用 set 指令创建自定义变量,或使用 map 指令根据一个变量的值来设置另一个变量的值。理解和善用这些变量是编写灵活和强大 Nginx 配置的基础。

3.9. 模块化配置: include 与代码片段

随着配置的增长,将所有内容都放在单个 nginx.conf 文件中会变得难以管理。Nginx 通过 include 指令支持模块化配置,允许将配置分割到多个文件中 18。

  • include 指令

    • 语法:include file_or_glob_pattern;
    • 行为:将指定文件或匹配通配符模式的所有文件的内容插入到 include 指令所在的位置。
    • 常见用法:
    • include /etc/nginx/mime.types; (在 http 块中)
    • include /etc/nginx/conf.d/*.conf; (在 http 块中,加载所有位于 conf.d 目录下以 .conf 结尾的文件) 23。这通常用于全局 HTTP 相关的配置,如上游服务器定义、日志格式、缓存路径等。
    • include /etc/nginx/sites-enabled/*; (在 http 块中,常见于 Debian/Ubuntu 系统,加载所有启用的站点配置文件) 18。
  • 目录结构最佳实践

    • nginx.conf:主配置文件,包含 maineventshttp 块的顶层指令。http 块通常会 include 其他配置文件。

    • conf.d/:用于存放与特定功能相关的全局配置片段,例如 gzip.conf, ssl_params.conf,或者第三方模块的配置。这些文件通常被 nginx.conf 中的 httpinclude

    • sites-available/ (Debian/Ubuntu 约定):存放每个虚拟主机 (站点) 的完整 server 块配置文件。例如,example.com.conf

    • sites-enabled/

      (Debian/Ubuntu 约定):通过在

    sites-available/

    目录中相应配置文件的符号链接来启用站点。Nginx 只会加载

    sites-enabled/

    中的配置。

    • 启用站点:sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/example.com.conf

    • 禁用站点:sudo rm /etc/nginx/sites-enabled/example.com.conf

    • snippets/ (Debian/Ubuntu 约定):存放可重用的配置片段,这些片段可以在多个 serverlocation 块中通过 include 指令引用 18。例如,可以创建一个 ssl-params.conf 文件包含推荐的 SSL/TLS 设置,然后在每个需要 HTTPS 的 server 块中 include snippets/ssl-params.conf;

    这种模块化的方法不仅使配置文件更整洁、易于阅读和理解,而且极大地提高了可维护性和可扩展性。当管理多个站点或复杂的配置时,能够独立修改和管理各个部分变得至关重要。例如,更新 SSL 配置时,只需修改一个共享的 ssl-params.conf 片段,而不是编辑每个站点的配置文件。这也便于团队协作,不同的成员可以负责配置的不同部分。对于版本控制系统 (如 Git) 来说,小的、专注的文件也更容易跟踪变更。

第四章:Nginx 作为反向代理

Nginx 最强大的功能之一就是作为反向代理服务器。本章将详细介绍如何配置 Nginx 来代理 HTTP/HTTPS 请求、WebSocket 连接,并讨论相关的头部处理、SSL 终止和缓冲机制。

4.1. 反向代理简介

反向代理(Reverse Proxy)是一种位于客户端和后端服务器之间的服务器。它接收来自客户端的网络请求,然后将这些请求转发到一个或多个后端服务器进行处理,最后将从后端服务器收到的响应返回给客户端,就好像响应是直接来自反向代理服务器本身一样 5。

使用 Nginx 作为反向代理可以带来诸多好处:

  • 负载均衡:将客户端请求分发到多个后端服务器,以分担负载,提高应用程序的吞吐量和可靠性 5。
  • 提高安全性:隐藏后端服务器的 IP 地址和特性,使其不直接暴露在互联网上,从而减少受攻击面。Nginx 还可以提供如 SSL 终止、IP 黑白名单、请求过滤等安全功能 5。
  • SSL/TLS 终止:Nginx 可以处理传入的 HTTPS 连接,解密请求,然后将未加密的 HTTP 请求转发到内部后端服务器。这减轻了后端应用服务器处理 SSL/TLS 加解密的负担,简化了证书管理 3。
  • 缓存静态和动态内容:Nginx 可以缓存来自后端服务器的响应内容,对于后续相同的请求,可以直接从缓存中提供响应,从而加快响应速度并减少后端服务器的负载 4。
  • 压缩:Nginx 可以在将响应发送给客户端之前对其进行压缩 (如 Gzip 或 Brotli),以减少传输数据量,加快页面加载速度 5。
  • 统一入口点:为多个不同的后端服务(可能是用不同技术栈构建的微服务)提供一个统一的访问入口和域名。
  • 请求路由与重写:根据 URL、头部或其他请求特征将请求路由到不同的后端服务,或在转发前修改请求。

4.2. 基本反向代理配置

配置 Nginx 作为反向代理的核心是 proxy_pass 指令和相关的头部设置。

4.2.1. proxy_pass 指令

proxy_pass 指令用于将请求转发到指定的后端服务器或上游服务器组 (upstream group) 5。它通常用在 location 块中。

  • 语法:proxy_pass URL;

    这里的 URL 可以是后端服务器的地址 (如 http://127.0.0.1:8000http://backend.example.com),也可以是预定义的 upstream 块的名称 (如 http://my_upstream_group)。

  • URI 传递与尾部斜杠的重要性 46:

    proxy_pass 指令中 URL 的末尾是否有斜杠 (/) 会显著影响传递给后端服务器的 URI。这是一个非常关键且容易混淆的点。

    1. proxy_pass URL 不带 URI 路径 (通常也没有尾部斜杠):

      如果 proxy_pass 指定的 URL 不包含 URI 路径 (例如 http://backend_server),则原始请求 URI (或匹配 location 后的部分) 会被完整地传递给后端。

      Nginx

      location /some/path/ {
       proxy_pass http://backend_server;
       # 请求 /some/path/page.html -> http://backend_server/some/path/page.html
      }
      location / {
       proxy_pass http://another_backend;
       # 请求 /any/uri -> http://another_backend/any/uri
      }
    2. proxy_pass URL 带 URI 路径 (通常意味着有尾部斜杠,或者路径后还有内容):

      如果 proxy_pass 指定的 URL 包含 URI 路径 (例如 http://backend_server/api/http://backend_server/),则匹配 location 的那部分请求 URI 会被 proxy_pass URL 中的路径替换掉。

      Nginx

      location /app/ {
       proxy_pass http://backend_server/;
       # 请求 /app/user/profile -> http://backend_server/user/profile
       # (location 匹配的 /app/ 被 proxy_pass 的 / 替换了)
      }
      location /v1/api/ {
       proxy_pass http://api_gateway/new_api/;
       # 请求 /v1/api/resource -> http://api_gateway/new_api/resource
       # (location 匹配的 /v1/api/ 被 proxy_pass 的 /new_api/ 替换了)
      }

      如果 location/,并且 proxy_pass 带有路径,例如 location / { proxy_pass http://backend/prefix/; },则请求 /page 会被代理到 http://backend/prefix/page

    下表总结了 proxy_pass 中尾部斜杠对 URI 转换的影响 (假设请求为 http://localhost/path/file):

location proxy_pass 后端收到的 URI
/path/ http://backend /path/file
/path/ http://backend/ /file
/path/ http://backend/newpath /newpathfile
/path/ http://backend/newpath/ /newpath/file
**注意**:当在 `proxy_pass` 中使用变量时 (例如 `proxy_pass $upstream_url;`),Nginx 的行为会有所不同。它不会自动传递原始 URI,您通常需要显式地附加 `$request_uri` (包含查询参数) 或 `$uri` (不含查询参数)。此外,使用变量时,Nginx 需要一个 `resolver` 指令来解析域名(即使是 `localhost` 或 IP 地址,这有些反直觉)[46]。
​```nginx
resolver 8.8.8.8; # 或者内部 DNS 服务器
location / {
    set $my_backend http://127.0.0.1:8000;
    proxy_pass $my_backend$request_uri; # 必须手动附加 $request_uri
}
​```

4.2.2. 向后端传递头信息

当 Nginx 将请求代理到后端服务器时,它会修改或设置一些 HTTP 头部。使用 proxy_set_header 指令可以自定义这些头部 47。这对于后端应用正确处理请求至关重要。

常用的 proxy_set_header 配置:

  • proxy_set_header Host $host;

    将原始客户端请求中的 "Host" 头部传递给后端服务器。许多后端应用依赖此头部来识别目标虚拟主机或生成正确的 URL。$host 变量的值是原始请求中的主机名,或者如果原始请求没有 Host 头,则是匹配的 server_name。

  • proxy_set_header X-Real-IP $remote_addr;

    将客户端的真实 IP 地址通过 "X-Real-IP" 头部传递给后端。$remote_addr 是直接连接到 Nginx 的客户端的 IP 地址。

  • proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    这是一个标准的头部,用于记录请求经过的代理服务器链上的客户端 IP 地址。$proxy_add_x_forwarded_for 变量会获取客户端的 $remote_addr,并将其附加到请求中已有的 "X-Forwarded-For" 头部值的末尾(如果存在)。如果后端服务器位于多个代理之后,这个头部非常有用。

  • proxy_set_header X-Forwarded-Proto $scheme;

    将原始请求的协议方案 ("http" 或 "https") 通过 "X-Forwarded-Proto" 头部传递给后端。当 Nginx 进行 SSL 终止(即客户端通过 HTTPS 连接到 Nginx,Nginx 再通过 HTTP 连接到后端)时,这个头部对于后端应用判断原始连接是否安全非常重要。例如,后端应用可能需要根据此头部生成 https:// 或 http:// 的 URL。

  • proxy_set_header X-Forwarded-Host $host;

    传递原始的 Host 头部,与 Host $host; 类似,但使用 X-Forwarded-Host 这个约定俗成的头部名称。

  • proxy_set_header X-Forwarded-Port $server_port;

    传递 Nginx 接收请求的端口。

  • proxy_set_header Connection "";

    通常用于禁用从 Nginx 到后端的长连接,强制 Nginx 为每个请求建立新连接。在某些情况下可能需要,但通常 Nginx 会管理与后端的连接(例如通过 upstream 的 keepalive)。

一个典型的反向代理 location 块可能如下:

Nginx

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

    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_read_timeout 60s;
}

正确设置这些头部对于确保后端应用程序的兼容性和安全性至关重要。例如,如果后端应用根据 Host 头部进行路由或生成链接,而 Nginx 没有正确传递它,应用可能会出错。同样,如果后端应用依赖客户端 IP 进行日志记录、访问控制或地理定位,那么 X-Real-IPX-Forwarded-For 头部就必不可少。

4.3. 代理 WebSocket 连接

WebSocket 是一种允许在客户端和服务器之间进行双向、全双工通信的协议,常用于实时应用如聊天、游戏、实时数据更新等。Nginx 可以作为反向代理来处理 WebSocket 连接。

WebSocket 连接的建立始于一个 HTTP 请求,该请求包含特殊的 UpgradeConnection 头部,请求服务器将连接从 HTTP 升级到 WebSocket 协议。Nginx 需要正确处理这些 "hop-by-hop" 头部,并将它们传递给后端 WebSocket 服务器 11。

配置 Nginx 代理 WebSocket 连接的关键指令如下 11:

  • proxy_http_version 1.1;:WebSocket 协议要求使用 HTTP/1.1 或更高版本进行握手。
  • proxy_set_header Upgrade $http_upgrade;:将客户端请求中的 Upgrade 头部传递给后端服务器。$http_upgrade 变量的值来自客户端的 Upgrade 头部。
  • proxy_set_header Connection "Upgrade";:将 Connection 头部设置为 "Upgrade" (或者有些文档写 'upgrade'),通知后端服务器这是一个升级请求。注意,这里的值是硬编码的 "Upgrade",而不是 $http_connection,因为 Connection 头部是逐跳的,Nginx 需要明确指示它希望将连接升级。
  • proxy_set_header Host $host;:传递原始 Host 头部。
  • proxy_cache_bypass $http_upgrade;:对于 WebSocket 升级请求,应绕过任何可能配置的缓存。

示例配置 11:

Nginx

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

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

    location /socket/ {
        proxy_pass http://backend_websocket_server; # 后端 WebSocket 服务器地址
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        # proxy_set_header Connection "Upgrade"; # 直接使用 "Upgrade"
        proxy_set_header Connection $connection_upgrade; # 使用 map 指令更灵活

        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;

        # 增加超时时间,因为 WebSocket 连接通常是长连接
        proxy_read_timeout 86400s; # 例如,24小时
        proxy_send_timeout 86400s;

        proxy_buffering off; # 对于长轮询或 WebSocket,有时关闭缓冲更好
    }
}

在上面的示例中,map 指令用于更优雅地处理 Connection 头部。如果客户端发送了 Upgrade 头部,$connection_upgrade 变量会被设置为 upgrade;否则,它会被设置为 close

对于长连接(如 WebSocket),调整 proxy_read_timeoutproxy_send_timeout 非常重要,以防止 Nginx 因长时间无数据传输而过早关闭连接。默认的超时时间(如 60 秒)对于 WebSocket 来说可能太短。同时,proxy_buffering off; 有时也被推荐用于 WebSocket,以确保消息能够尽快在客户端和服务器之间传递,尽管这可能会增加 Nginx 的负载,因为它需要为每个连接保持更活跃的状态。

4.4. 为后端服务器配置 SSL/TLS 终止

SSL/TLS 终止是指 Nginx 处理与客户端之间的 HTTPS 加密连接,然后将解密后的 HTTP 请求转发到内部的后端服务器。这种做法有几个优点:

  • 减轻后端服务器负担:SSL/TLS 加解密是 CPU 密集型操作。将其卸载到 Nginx 可以让后端应用服务器专注于业务逻辑。
  • 集中管理 SSL 证书:只需在 Nginx 服务器上配置和更新 SSL 证书,而无需在每个后端服务器上都进行配置。
  • 简化后端配置:后端服务器可以配置为仅监听 HTTP,简化其部署和管理。

配置步骤:

  1. 在 Nginx server 块中启用 SSL

    • 使用 listen 443 ssl; 指令。
    • 配置 ssl_certificatessl_certificate_key 指令,指向您的 SSL 证书文件和私钥文件 39。
    • 配置其他推荐的 SSL 参数,如 ssl_protocols, ssl_ciphers, ssl_prefer_server_ciphers 等(详见第七章安全加固)。
  2. 使用 proxy_pass 将请求转发到后端的 HTTP 服务:

    proxy_pass 指令应指向后端服务器的 HTTP 地址 (例如 http://backend_server_ip:port)。

  3. 传递 X-Forwarded-Proto 头部:

    为了让后端服务器知道原始客户端请求是 HTTPS,务必设置 proxy_set_header X-Forwarded-Proto $scheme;。$scheme 变量的值将是 "https"。

示例配置 5:

Nginx

server {
    listen 80;
    server_name example.com;
    # 推荐将所有 HTTP 请求重定向到 HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2; # 启用 HTTP/2 以获得更好性能
    server_name example.com;

    # SSL 证书配置
    ssl_certificate /etc/nginx/ssl/example.com.fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/example.com.privkey.pem;

    # 其他 SSL/TLS 安全增强配置 (见第七章)
    # ssl_protocols TLSv1.2 TLSv1.3;
    # ssl_ciphers '...';
    # ssl_prefer_server_ciphers on;
    #...

    location / {
        proxy_pass http://127.0.0.1:8080; # 后端应用服务器监听在 HTTP 8080 端口

        # 传递必要的头部信息
        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_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
    }
}

通过这种方式,客户端与 Nginx 之间是安全的 HTTPS 通信,而 Nginx 与后端服务器之间可以是效率更高的 HTTP 通信(假设内部网络是安全的)。

4.5. 代理连接的缓冲与超时

Nginx 在代理请求时,可以对来自后端服务器的响应进行缓冲。

  • 响应缓冲 (Response Buffering)

    • proxy_buffering on | off;

    :启用或禁用来自后端服务器响应的缓冲。默认为

    on

    • 当启用时 (on),Nginx 会尽可能快地从后端服务器接收响应,并将其存储在缓冲区中。一旦缓冲区填满或响应接收完毕,Nginx 才开始将数据发送给客户端。这可以释放后端服务器,使其能更快处理其他请求,并可能改善客户端的感知性能(如果客户端网络较慢,Nginx 可以先完整接收响应)。

    • 当禁用时 (off),Nginx 在收到后端响应数据后会立即将其同步发送给客户端,不进行缓冲。这对于需要尽快将数据传递给客户端的长轮询 (long-polling) 或流式响应 (如服务器发送事件 SSE) 可能更有用,但会使 Nginx 连接和后端连接的持续时间更长。

    • proxy_buffer_size <size>;:设置用于读取从代理服务器接收到的响应的第一部分的缓冲区大小。这部分通常包含响应头。默认通常是 4k 或 8k。

    • proxy_buffers <number> <size>;:设置用于读取响应的缓冲区的数量和大小。例如 proxy_buffers 8 16k; 表示使用 8 个 16KB 的缓冲区。

    • proxy_busy_buffers_size <size>;:限制在响应尚未完全读取时可能正忙于向客户端发送数据的缓冲区的总大小。

    缓冲行为是一个权衡:启用缓冲可以提高后端服务器的并发处理能力,并可能对慢速客户端更友好,但会增加 Nginx 的内存消耗和一点点延迟。禁用缓冲则适用于需要低延迟实时数据流的场景。

  • 超时 (Timeouts):

    正确配置超时对于防止连接因后端服务器响应缓慢或网络问题而无限期挂起至关重要。

    • proxy_connect_timeout <time>;:定义与后端服务器建立连接的超时时间。默认通常是 60 秒。如果在此时间内未能建立连接,Nginx 会返回 504 Gateway Timeout 错误 (或根据 proxy_next_upstream 尝试下一个服务器)。
    • proxy_send_timeout <time>;:定义向后端服务器发送请求的超时时间。这是指两次连续写操作之间的时间间隔,而不是整个请求的传输时间。如果后端在此时间内没有接收到任何数据,连接将被关闭。
    • proxy_read_timeout <time>;:定义从后端服务器读取响应的超时时间 51。这是指两次连续读操作之间的时间间隔,而不是整个响应的接收时间。如果后端在此时间内没有发送任何数据,连接将被关闭,Nginx 通常会返回 504 Gateway Timeout。对于可能需要较长时间处理的请求 (如大文件生成或复杂计算),可能需要增加此值。

    示例:

    Nginx

    location /long_process/ {
      proxy_pass http://backend_service;
      proxy_connect_timeout 10s;
      proxy_read_timeout 300s; # 允许后端处理长达5分钟
      proxy_send_timeout 60s;
    
      proxy_buffering on; # 或者 off,取决于需求
    }

    仔细调整这些缓冲和超时参数,对于构建稳定和高性能的反向代理至关重要。

第五章:Nginx 实现负载均衡

负载均衡是将网络流量有效地分配到多个后端服务器的过程,以确保没有单个服务器承担过多负载。Nginx 作为一个功能强大的反向代理,也提供了灵活的负载均衡能力,可以提高应用程序的可用性、可伸缩性和整体性能 5。

5.1. 负载均衡简介

当单个应用服务器无法处理所有传入请求时,就需要负载均衡。通过在多个服务器实例之间分配请求,负载均衡可以:

  • 提高可用性:如果一个后端服务器发生故障,负载均衡器可以将流量自动重定向到其他健康的服务器,从而减少停机时间。
  • 增强可伸缩性:可以根据流量需求轻松地添加或移除后端服务器,而无需更改客户端的访问点。
  • 优化性能:通过将负载分散到多个服务器,可以减少每个服务器的压力,从而降低响应延迟并提高吞吐量。

Nginx 使用其 ngx_http_upstream_module 模块来实现 HTTP 负载均衡。

5.2. upstream 模块

upstream 指令用于定义一组后端服务器,这些服务器将共同处理被代理的请求。这个指令块通常定义在 http 上下文中 5。

  • 语法

    Nginx

    http {
      upstream  {
          # server directives and load balancing method directives go here
          server backend1.example.com:8080;
          server 192.168.1.101:80;
          server unix:/tmp/backend.sock;
          #... more servers...
      }
    
      server {
          listen 80;
          server_name myapp.example.com;
    
          location / {
              proxy_pass http://; # 引用 upstream 块的名称
          }
      }
    }

    52

    在上面的例子中, 是您为这组后端服务器指定的任意名称 (例如 myapp_backend 或 api_servers)。然后在 location 块的 proxy_pass 指令中通过 http:// 来引用这个服务器池。

  • server 指令 (在 upstream 块内):

    用于定义池中的每个后端服务器。它可以指定服务器的地址和端口,或者一个 Unix 套接字路径 52。

    server address [parameters];

    常见的参数包括 weight, max_fails, fail_timeout, backup, down (稍后详述)。

5.3. 负载均衡算法

Nginx 支持多种负载均衡算法,用于决定将传入请求发送到 upstream 池中的哪个服务器。

算法 (Method) 描述 Nginx 指令 (在 upstream 块内) 适用场景
轮询 (Round Robin) (默认算法) 按顺序将请求轮流分配给后端服务器。如果服务器配置了权重,则按权重比例分配 6。 (无,或可显式指定 round_robin;) 服务器性能相近,请求处理时间相似的无状态应用。
最少连接 (Least Connections) 将新请求发送到当前活动连接数最少的服务器 6。如果配置了权重,会考虑权重。 least_conn; 请求处理时间差异较大,或会话持续时间较长的应用,有助于防止单个服务器过载。
IP 哈希 (IP Hash) 根据客户端 IP 地址的哈希值选择服务器。确保来自同一客户端的请求始终被定向到同一台后端服务器 5。 ip_hash; 需要会话保持 (session persistence 或 "sticky sessions") 的应用,例如用户状态存储在特定应用服务器上的场景。
通用哈希 (Generic Hash) 根据用户定义的键 (可以是文本、变量或其组合,如 $request_uri$cookie_some_cookie) 的哈希值选择服务器 52。 hash <key> [consistent]; 需要更灵活的会话保持机制,或希望基于请求的特定属性 (如 URL) 来实现缓存一致性或请求分发。consistent 参数使用 Ketama 一致性哈希算法,当服务器列表变化时,能最小化键的重新映射。

5.3.1. 轮询 (Round Robin) (默认)

这是 Nginx 默认的负载均衡方法。请求会按顺序依次分配给 upstream 块中列出的服务器 6。如果服务器配置了不同的 weight (权重) 参数,Nginx 会根据权重比例分配请求。例如,权重为 2 的服务器接收到的请求大约是权重为 1 的服务器的两倍。

Nginx

upstream backend_servers {
    server app1.example.com;       # 权重默认为 1
    server app2.example.com weight=3; # app2 接收约 3 倍于 app1 的请求
    server app3.example.com;
}

5.3.2. 最少连接 (least_conn)

使用 least_conn 指令后,Nginx 会将新的请求发送到当前拥有最少活动连接数的服务器 6。这种方法在处理时间可能显著不同的请求时非常有效,因为它有助于将负载更均匀地分配给那些处理速度更快或当前负载较轻的服务器。

Nginx

upstream backend_servers {
    least_conn;
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}

如果服务器也配置了权重,Nginx 在计算“最少连接”时会考虑权重,即 active_connections / weight 的值最小者优先。

5.3.3. IP 哈希 (ip_hash)

ip_hash 方法确保来自特定客户端 IP 地址的请求总是被定向到同一台后端服务器(只要该服务器可用)5。Nginx 使用客户端 IP 地址的前三个字节 (对于 IPv4) 或整个地址 (对于 IPv6) 来计算哈希值,从而确定目标服务器。

Nginx

upstream backend_servers {
    ip_hash;
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}

这种方法对于需要会话保持的应用非常有用,例如,当用户的会话数据存储在特定的应用服务器本地时。但是,它也可能导致负载分配不均,特别是当少数几个 IP 地址产生大量请求,或者大量用户通过同一个 NAT 网关访问时。如果使用 ip_hash 的服务器需要临时移除,必须将其标记为 down,以保持现有客户端的会话亲和性。

5.3.4. 通用哈希 (hash)

hash 方法允许您指定一个自定义的键,Nginx 将根据此键的哈希值来选择后端服务器 52。这个键可以是文本、Nginx 变量或它们的组合。

Nginx

upstream backend_servers {
    hash $request_uri consistent; # 基于请求 URI 进行哈希,consistent 启用一致性哈希
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}

使用 $request_uri 作为键可以提高缓存命中率,因为对同一 URI 的请求会被定向到同一服务器。consistent 参数启用 Ketama 一致性哈希算法。当后端服务器列表发生变化(例如添加或移除服务器)时,一致性哈希能确保只有一小部分键需要重新映射到不同的服务器,从而最大限度地减少对现有会话或缓存的影响。

5.4. 服务器权重与参数

upstream 块内的 server 指令可以接受多个参数来调整其行为和参与负载均衡的方式:

  • weight=number:设置服务器的权重,默认为 1 6。权重越高的服务器,在轮询和最少连接算法中(经过调整后)会接收到更多的请求。这对于处理能力不同的服务器非常有用。

    Nginx

    server backend1.example.com weight=5; # 接收5倍流量
    server backend2.example.com;          # 接收1倍流量 (默认)
  • max_conns=number:(Nginx Plus 功能,或某些第三方模块) 限制到该服务器的最大并发活动连接数。当达到此限制时,新请求将被放入队列或传递给其他服务器。

  • max_fails=number:在 fail_timeout 时间段内,与服务器通信的连续不成功尝试的最大次数,超过此次数则认为服务器不可用。默认为 1 52。设置为 0 会禁用失败尝试的计数。

  • fail_timeout=time

    1. max_fails 结合使用,指定将服务器标记为不可用的时间长度,在此期间不会向其发送请求。默认为 10 秒 52。
    2. 也指定了服务器被认为是失败的连接尝试所持续的时间。
  • backup:将服务器标记为备份服务器 52。只有当所有非备份(主)服务器都不可用时,请求才会被传递给备份服务器。

    Nginx

    server primary1.example.com;
    server primary2.example.com;
    server backup1.example.com backup;
  • down:将服务器标记为永久不可用。通常用于计划内维护,以便平滑地将服务器从负载均衡池中移除,而不会影响现有配置。

    Nginx

    server backend1.example.com down;
  • resolve:(Nginx 1.9.13+,通常与 Nginx Plus 或特定配置相关) 监控与服务器域名对应的 IP 地址的变化,并自动修改上游配置而无需重启 Nginx。需要 resolver 指令在 http 块或 upstream 块中定义。

5.5. 健康检查

健康检查是负载均衡系统中的一个重要组成部分,用于确定后端服务器是否能够正常处理请求。

  • 被动健康检查 (Passive Health Checks):

    Nginx 默认执行被动健康检查。当 Nginx 尝试将请求传递给后端服务器时,如果连接超时、服务器返回错误或在指定时间内没有响应,Nginx 会认为这是一次失败的尝试。根据 max_fails 和 fail_timeout 参数的设置,如果失败次数达到阈值,Nginx 会在 fail_timeout 期间将该服务器标记为不可用,并避免向其发送新的请求 5。过了 fail_timeout 时间后,Nginx 会再次尝试向该服务器发送请求。

  • 主动健康检查 (Active Health Checks):

    主动健康检查涉及负载均衡器定期向后端服务器发送特定的健康检查请求(例如,请求一个特定的 URL 并期望一个 200 OK 响应),以主动确定其健康状况。

    • Nginx 开源版:本身不直接提供复杂的主动健康检查功能。可以通过第三方模块 (如 nginx_upstream_check_module) 或结合外部脚本和 Nginx API (如果可用) 来实现。
    • Nginx Plus:提供了内置的主动健康检查功能。可以配置健康检查的频率、URI、期望的响应状态码和内容等。

    有效的健康检查机制对于确保只有健康的服务器接收流量至关重要,从而提高整体服务的可靠性。

  • proxy_next_upstream 指令:

    此指令(用在 location 或 server 块中)定义了在哪些类型的错误发生时,Nginx 应该将请求传递给上游服务器池中的下一个服务器。

    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;

    默认情况下,error 和 timeout 会触发尝试下一个服务器。您可以添加其他 HTTP 状态码。non_idempotent 参数允许将 POST、LOCK、PATCH 等非幂等请求在发生错误时也传递给下一个服务器(需谨慎使用)。

5.6. 使用 ip_hash 实现会话保持

如前所述,ip_hash 负载均衡算法通过确保来自同一客户端 IP 的请求始终被路由到同一后端服务器,从而实现会话保持(或称“粘性会话”)53。

这对于那些将会话状态(如用户登录信息、购物车内容)存储在应用服务器本地内存中,而不是共享会话存储(如 Redis 或数据库)的应用来说是必需的。如果用户的请求在会话期间被路由到不同的服务器,他们可能会丢失会话信息。

配置示例

Nginx

http {
    upstream my_sticky_app {
        ip_hash;
        server app_server1.example.com;
        server app_server2.example.com;
        server app_server3.example.com;
    }

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

        location / {
            proxy_pass http://my_sticky_app;
            # 其他 proxy_set_header 等指令
        }
    }
}

ip_hash 的注意事项

  • 负载可能不均:如果少数 IP 地址产生了不成比例的大量请求,或者大量用户通过同一个大型 NAT(网络地址转换)设备访问,可能会导致某些后端服务器负载过高,而其他服务器负载较轻。
  • 服务器移除:如果使用 ip_hash 的服务器池中的某个服务器需要下线维护,应将其标记为 down。直接从配置中移除该服务器会导致哈希表重新计算,从而可能将许多现有客户端的会话重定向到新的服务器,导致会话丢失。
  • 客户端 IP 变化:如果客户端的 IP 地址在会话期间发生变化(例如,移动设备在不同网络间切换),会话亲和性将丢失。

对于更复杂的会话保持需求,例如基于 Cookie 或其他请求属性,可能需要使用 Nginx Plus 的高级会话持久性功能或第三方模块,或者在应用层面实现共享会话存储。

第六章:Nginx 性能优化

Nginx 以其高性能而闻名,但通过仔细的配置调优,还可以进一步挖掘其潜力,以应对高流量负载并提供更快的用户体验。本章将探讨一系列性能优化技术。

6.1. 优化 Worker 进程

Nginx 的 Worker 进程是实际处理客户端请求的主力。合理配置 Worker 相关的参数对于充分利用服务器资源至关重要。

  • worker_processes <number | auto>;
    • 此指令设置 Nginx 启动的 Worker 进程数量 15。
    • 通常建议将此值设置为服务器的 CPU 核心数,以最大限度地利用多核处理能力,并减少不必要的上下文切换 16。设置为 auto 会让 Nginx 尝试自动检测 CPU 核心数 17。
    • 对于主要进行 I/O 密集型操作(如服务大量静态文件或作为反向代理)的 Nginx 实例,Worker 数量等于 CPU 核心数通常是最佳选择。
    • 如果 Nginx 还承担了大量 CPU 密集型任务(如 SSL/TLS 加解密、Gzip 压缩),在某些情况下,略多于 CPU 核心数的 Worker 进程(例如 1.5 倍或 2 倍)可能有助于提高吞吐量,但这需要通过实际测试来验证,以避免过多的上下文切换反而降低性能。
  • worker_rlimit_nofile <number>;
    • 此指令设置每个 Worker 进程可以打开的最大文件描述符数量 (RLIMIT_NOFILE) 17。
    • 每个客户端连接至少需要一个文件描述符(如果 Nginx 作为反向代理,则通常是两个:一个用于客户端连接,一个用于到后端服务器的连接)。此外,Nginx 还需要文件描述符来打开静态文件、日志文件等。
    • 因此,worker_rlimit_nofile 的值应远大于 worker_connections 的值。一个常见的建议是将其设置为 worker_connections 的两倍或更多,例如,如果 worker_connections 是 10000,则 worker_rlimit_nofile 可以设置为 20000 或更高 17。
    • 还需要确保操作系统的文件描述符限制也足够高。可以通过 ulimit -n 查看当前用户的限制,并通过修改 /etc/security/limits.conf (针对用户/进程) 和 /etc/sysctl.conf (例如 fs.file-max,针对系统全局) 来调整系统级限制 55。
  • worker_cpu_affinity auto | <cpumask...>; (可选高级调优)
    • 允许将 Worker 进程绑定到特定的 CPU 核心。例如,worker_cpu_affinity auto; 会尝试为每个 Worker 进程分配一个单独的 CPU 核心。
    • worker_cpu_affinity 0001 0010 0100 1000; (对于4核CPU,将4个 Worker 分别绑定到不同核心)。
    • 这可以减少 CPU 缓存失效和上下文切换,从而在某些高负载场景下提高性能,但配置较为复杂,需要仔细测试。

这些 Worker 相关的设置之间存在密切的依赖关系。例如,服务器能够处理的最大并发客户端数约等于 worker_processes * worker_connections。而 worker_rlimit_nofile 必须能够支持 worker_connections 所需的文件描述符数量,否则 Worker 进程在达到文件描述符上限时将无法接受新的连接或打开文件,导致 "too many open files" 错误。因此,在调整一个参数时,通常需要考虑其他相关参数的影响。

6.2. 优化连接处理

高效地管理客户端和上游服务器的连接对于 Nginx 的性能至关重要。

  • worker_connections <number>;
    • 此指令位于 events 块中,定义了每个 Worker 进程可以同时打开的最大连接数 [15

Leave a Comment

您的电子邮箱地址不会被公开。 必填项已用*标注

close
arrow_upward