MD原文档下载:百度网盘
一、HTTP协议
1.1 HTTP 协议基础
HTTP (超文本传输协议) : 浏览器和服务器之间的通信语言
基于 TCP / IP 传递数据
HTTP 属于 7 层 (应用层) 协议 , TCP 属于 4 层 (传输层) 协议 , IP 属于 3 层 (网络层) 协议
HTTP 协议工作在 客户端 - 服务器 (Client - Server) 架构 , 浏览器作为 HTTP 客户端 , 通过 URL 向 HTTP 服务端 (Web 服务器) 发送请求
1.2 HTTP 三大特点
1.2.1 无连接
在 HTTP 原始设计中,每次请求结束,服务器都会主动断开 TCP 连接
每个请求都需要 建立/断开连接
Nginx 的 keepalive_timeout 配置项 可优化 HTTP 无连接设计,保持连接指定时间,指定时间内客户端发来新请求,复用这条连接
1.2.2 媒体独立
服务器不绑定、不限制客户端只能接收某一种固定格式的数据,由客户端通过请求头主动声明自己能接受哪些媒体类型,服务器按需返回对应资源
请求头的 Accept 声明客户端请求的资源类型
响应头的 Content-Type 声明服务端返回的资源类型
1.2.3 无状态
HTTP 协议对事物处理没有记忆能力,每次请求都是独立的,服务器不会保留之前请求的信息
引入 Cookie / Session 机制来记住用户
无状态 是 Web 服务器 水平扩展(横向扩容)的基础
1.3 HTTP 请求 和 响应 报文结构
1.3.1 组成部分
- 起始行
- 首部字段
- 消息正文
1.3.2 请求报文
由 请求行、请求头、空行、请求体 构成
curl -v jd.com
* Host jd.com:80 was resolved.
* IPv6: (none)
* IPv4: 111.13.149.108, 211.144.27.126, 106.39.171.134, 211.144.24.218
* Trying 111.13.149.108:80...
* Connected to jd.com (111.13.149.108) port 80
* using HTTP/1.x
> GET / HTTP/1.1 # 请求行 (方法 + 路径 + 协议版本)
> Host: jd.com # 请求头 (键值对),此行表示请求的主机名
> User-Agent: curl/8.12.1 # 请求头 (键值对),此行表示用户使用的代理工具
> Accept: */* # 请求头 (键值对),此行表示客户端可接收的资源类型为任意
> # 空行 (分隔符,分隔首部字段和消息正文) # 空行下跟请求体,GET 方法为空,POST 方法为请求数据(例如用户名,密码,验证码)
* Request completely sent off
< HTTP/1.1 301 Moved Permanently
< Date: Fri, 31 Jul 2026 04:03:42 GMT
< Content-Type: text/html
< Content-Length: 178
< Connection: keep-alive
< Location: https://www.jd.com
< Server: jfe
< Cache-Control: no-cache
<
<html>
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
1.3.3 常见请求头
| 请求头 | 说明 | 备注 |
|---|---|---|
Host | 目标主机域名 | HTTP/1.1 必须携带,用于在一台服务器区分多个网站 |
User-Agent | 客户端标识 | 标识浏览器类型、版本、操作系统 |
Accept | 可接受的响应格式 | 告诉服务端客户端期望返回的文件格式 |
Cookie | 携带 Session ID | 浏览器自动携带,用于会话保持 |
1.3.4 响应报文
由 状态行、响应头、空行、响应体 构成
curl -v jd.com
* Host jd.com:80 was resolved.
* IPv6: (none)
* IPv4: 111.13.149.108, 211.144.27.126, 106.39.171.134, 211.144.24.218
* Trying 111.13.149.108:80...
* Connected to jd.com (111.13.149.108) port 80
* using HTTP/1.x
> GET / HTTP/1.1
> Host: jd.com
> User-Agent: curl/8.12.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 301 Moved Permanently # 状态行 (协议版本 + 状态码 + 描述)
< Date: Fri, 31 Jul 2026 04:03:42 GMT # 响应头 (键值对)
< Content-Type: text/html # 响应头 (键值对) 响应资源的类型
< Content-Length: 178 # 响应头 (键值对) 响应数据的长度
< Connection: keep-alive # 响应头 (键值对)
< Location: https://www.jd.com # 响应头 (键值对) 重定向指向的新主机名
< Server: jfe # 响应头 (键值对)
< Cache-Control: no-cache # 响应头 (键值对)
< # 空行 (分隔符,分隔响应头部和响应数据)
<html> # 此行及下方内容为 响应体 (实际传输的数据)
<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
1.3.5 常见响应头
| 响应头 | 说明 | 备注 |
|---|---|---|
Content-Type | 响应体格式 | 告诉浏览器如何解析内容 |
Content-Length | 响应体大小 | 响应体的字节数 |
Location | 重定向目标 | 配合 301 / 302 使用,指定跳转主机域名 |
Set-Cookie | 设置 Cookie | 服务端下发 Cookie 给客户端 |
X-Frame-Options | 防点击劫持 | 禁止页面嵌入 iframe |
X-Content-Type-Options | 防 MIME 嗅探 | 禁止浏览器自动推断文件类型 |
Server | 服务器软件信息 | 标识 Web 服务器类型和版本 |
Cache-Control | 缓存策略 | 告诉浏览器缓存多少秒 |
1.4 HTTP 请求方法
| 方法 | 含义 | 幂等性 | 使用场景 |
|---|---|---|---|
GET | 请求资源 | 是 | 查看列表,查看详情,下载文件 |
POST | 提交数据 | 否 | 创建订单,登录提交,发布评论 |
PUT | 全量更新 | 是 | 更新完整用户信息,替换文件 |
PATCH | 部分更新 | 否 | 修改手机号,修改状态 |
DELETE | 删除资源 | 是 | 删除用户,删除文件 |
OPTIONS | 预检请求 | 是 | 跨域请求前的探测,查询服务器支持的方法 |
幂等性: 多次执行同一操作,产生的结果相同
1.5 HTTP 状态码
类型:
| 分类 | 范围 | 说明 |
|---|---|---|
| 1xx | 100-199 | 信息提示 |
| 2xx | 200-299 | 成功 |
| 3xx | 300-399 | 重定向 |
| 4xx | 400-499 | 客户端错误 |
| 5xx | 500-599 | 服务端错误 |
高频状态码:
| 状态码 | 说明 | 备注 |
|---|---|---|
| 200 | 请求成功 | |
| 301 | 永久重定向 | 资源永久迁移到新地址,浏览器会缓存这个跳转,下次直接访问新地址 |
| 302 | 临时重定向 | 资源临时迁移到新地址,浏览器不会缓存,下次仍访问原地址 |
| 304 | 未修改(缓存命中) | 资源未修改,浏览器可用使用本地缓存 |
| 400 | 请求语法错误 | 服务端无法理解请求,通常是参数格式不对 |
| 401 | 未认证 | 请求需要用户身份认证 |
| 403 | 禁止访问 | 服务端理解请求,但拒绝执行.例如普通用户访问管理页,或访问文件 Nginx 无权限读取 |
| 404 | 资源不存在 | 服务端找不到请求的资源 |
| 429 | 请求过多(限流) | 客户端在限定时间发送了太多请求 |
| 500 | 服务器内部错误 | 服务端代码执行出错,需要查看后端服务日志(配置错误,依赖库异常) |
| 502 | 网关错误 | 网关从后端服务收到无效响应,与后端通信异常1例如后端 Java 服务宕机,Nginx 无法连接,检查后端是否存活,端口是否监听 |
| 503 | 服务不可用 | 服务端过载或正在维护,检查后端负载,限流配置 |
| 504 | 网关超时 | 网关等待后端服务响应超时,检查后端响应时间,调大 proxy_read_timeout |
1.6 Cookie 与 Session
用于记住用户的登录状态,解决 HTTP 的无状态
会话保持流程:
- 用户提交账号密码 → 服务端验证通过
- 服务端创建 Session (存储用户身份、权限等信息),生成唯一的 Session ID
- 服务端通过
Set-Cookie响应头将JSESSIONID=下发给浏览器 - 浏览器将 Session ID 保存在 Cookie 中
- 后续每次请求,浏览器自动通过 Cookie 请求头携带 Session ID
- 服务端根据 Session ID 找到对应 Session 数据 → 识别用户身份
Cookie 只能浏览器使用
1.6.1 生产环境 Session 存储问题
单机环境: Session 存本地内存 (默认) 或文件,没有问题
负载均衡(多台服务器): Session 集中存储 (Redis / 数据库)
1.6.2 Token 及 JWT
Token 分为普通 Token 和 特殊Token JWT
普通 Token 大部分机制与 Session 一致,只是返回首部头不一样,Token 同时支持微服务使用
JWT无需数据库, 生成JWT保存在客户端,服务端对 JWT 进行签名防篡改
1.7 HTTPS 与 TLS
HTTP 使用明文传输
HTTPS = HTTP + TLS
1.7.1 对称加密和非对称加密
| 对比纬度 | 对称加密 | 非对称加密 |
|---|---|---|
| 密钥 | 使用同一把密钥 | 加密使用公钥(公开),解密使用私钥(保密) |
| 速度 | 快 | 慢(比对称加密慢百倍以上) |
| 代表算法 | AES,ChaCha20,DES | RSA,ECC(椭圆曲线) |
| 用途 | 加密实际传输的数据 | 安全传递对称加密的密钥 |
实际应用是: 非对称密钥负责安全传递 “对称密钥” ,对称密钥负责快速加密所有数据
公钥负责加密,私钥负责解密
私钥负责签名,公钥负责验证
1.7.2 TLS 握手流程
- ClientHello : 客户端发送 支持的TLS版本、加密套件列表、一个随机数 (Client Random)
- ServerHello : 服务器回应 选定的加密套件、一个随机数 (Server Random)、服务端数字证书
- 客户端验证证书 : 浏览器验证证书链 (域名匹配、有效期、签发者可信、未吊销)
- 密钥交换 : 客户端生成 Pre-Master Secret,用服务端数字证书携带的公钥加密后发给服务器
- 生成会话密钥 : 双方用 Client Random + Server Random + Pre-Master Secret 计算出 会话密钥 (Master Secret)
- 加密通信 : 双方使用 会话密钥 开始加密 HTTP 请求和响应
数字证书包括: 域名 + 网站公钥 + CA 签名
通过 哈希摘要 + 非对称签名 实现防篡改
CA 使用私钥对 TBS(待签名数据) 计算的哈希摘要进行签名,将签名算法和签名值追加到 TBS 后方生成数字证书
1.7.3 证书链
| 证书类型 | 说明 | 签发者 | 存放位置 |
|---|---|---|---|
| 根 CA 证书 (Root CA) | 信任锚点,是整个信任链的起点 | 自签 | 浏览器 / 系统内置 (预置信任) |
| 中间 CA 证书 (Intermediate CA) | 根 CA 授权给中间 CA,用于签发服务器证书 | 根 CA | 随服务器证书一起下发 |
| 服务器证书 (Server Certificate) | 最终签发给域名的证书,包含 域名 + 服务端公钥 + CA 签名 | 中间 CA | Web 服务器上配置 |
通配符证书 : *.域名,匹配所有子域名,无需为每个子域名单独购买证书
中间 CA 的作用 : 根 CA 不用于直接签发服务器证书,只签发中间 CA,这样根 CA 可以离线保护根 CA 的私钥,更安全
二、Nginx
2.1 Nginx 相关概念
2.1.1 Nginx 定位
Nginx 可作为独立的 Web 服务端,更是 “流量入口”
Nginx 负责 接收请求、返回静态页面、转发动态请求,各种后端服务负责处理业务逻辑、操作数据库
2.1.2 nginx 架构
2.1.2.1 Master - Worker 进程
| 进程 | 职责 | 数量 | 关键特定 |
|---|---|---|---|
Master | 读取/校验配置,监听端口,管理 Worker 进程 | 1 | 以 root 用户运行,不处理客户端请求 |
Worker | 接收并处理客户端请求 | = CPU 核心数 | 以 nginx / www-data 用户运行,每个 Worker 独立,内存隔离 |
# 启动 Nginx 后查看进程树
ps auxf | grep nginx
2.1.2.2 epoll 事件驱动模型
三种内核 I / O 模型:
| 对比维度 | BIO (阻塞 IO) | select / poll | epoll |
|---|---|---|---|
| 并发连接数 | 低 (受线程数限制) | 中 (select 有 1024 限制) | 高(数万) |
| CPU 占用 | 高 (线程切换) | 高 (轮询扫描) | 低 (事件通知) |
| 内存占用 | 极高 (每个连接建立一个线程) | 低 | 低 |
| 时间复杂度 | O(n) | O(n) | O(1) |
| 适用场景 | 连接数 < 100 | 连接数 < 1000 | 高并发生产环境 |
| 使用应用 | Apache (prefork 模式) | 早期 Unix 网络程序 | Nginx、Redis |
Nginx 默认使用 epoll 非阻塞模型
并发连接数=RPS/响应时间
建立连接 和 事件处理
2.2 Nginx 部署
2.2.1 包安装
测试环境使用
# 建议安装基础工具
dnf install -y curl wget vim net-tools tar git
apt install -y curl wget vim git
# 测试环境关闭防火墙
systemctl disable --now firewalld
# 安装 nginx
dnf install -y nginx
apt update;apt install -y nginx
# 查看版本
nginx -v
# Rocky 启动服务
systemctl enable --now nginx
# 查看服务状态
systemctl status nginx
# 查看端口监听
ss -ntlp | grep nginx
2.2.2 编译安装
生产环境推荐,可自定义安装版本,可按需添加模块,路径标准化
编译时推荐 静态编译 , 模块嵌入 nginx 二进制程序,自动加载,模块与 nginx 版本精确匹配
测试环境推荐编译模块
| 模块 | 用途 | 使用场景 |
|---|---|---|
echo-nginx-module | 输出调试信息 | 测试 location 匹配,调试配置 |
headers-more-nginx-module | 修改 请求 / 响应头 | 添加、修改、删除 HTTP 头 |
nginx-http-flv-module | 视频流推送 | 直播推流,视频点播 |
ngx_brotli | Brotli 压缩 | 比 gizp 压缩率更高 |
nginx-module-vts | 流量统计 | 监控每个虚拟机的请求数、流量 |
# 安装编译依赖
dnf install -y gcc make pcre2-devel zlib-devel openssl-devel git
# 下载源码包和第三方模块
cd /usr/local/src/
wget https://nginx.org/download/nginx-1.30.4.tar.gz
git clone https://github.com/openresty/echo-nginx-module.git
# 创建用户
useradd -s /sbin/nologin -r nginx
# 解压缩
tar xf nginx-1.30.4.tar.gz
# 静态编译
cd nginx-1.30.4/
./configure \
--prefix=/usr/local/nginx \
--sbin-path=/usr/sbin/nginx \
--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 \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--add-module=/usr/local/src/echo-nginx-module
# 编译安装
make -j$(nproc)
make install
# 查看编译参数验证模块是否编译成功
/usr/sbin/nginx -V
# 创建 systemd 服务文件
tee /etc/systemd/system/nginx.service <<'EOF'
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target
[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
LimitNOFILE=100000
[Install]
WantedBy=multi-user.target
EOF
# 启动服务
systemctl daemon-reload
systemctl enable --now nginx
# 查看服务状态
systemctl status nginx
# 查看端口监听
ss -ntlp | grep nginx
脚本编译安装
文件见网盘
# 进入安装文件放置目录
cd /usr/local/src/
# 放入安装文件
# 运行安装脚本
bash install_nginx.sh
2.3 nginx 原生管理方式
# 直接启动 Nginx
/usr/sbin/nginx
# 强制终止 Nginx
/usr/sbin/nginx -s stop
# 优雅停止 Nginx (完成当前请求后退出)
/usr/sbin/nginx -s quit
# 重载配置 (不重启服务重新加载)
/usr/sbin/nginx -s reload
# 重新打开日志文件 (用于日志切割)
/usr/sbin/nginx -s reopen
2.4 Nginx 平滑升级
在不停止 Nginx 服务的情况下,替换原有二进制文件
原理:
- 旧 Master 进程启动新 Master 进程
- 新 Master 进程启动新 Woeker 进程
- 新 Worker 进程接管新连接
- 旧 Worker 进程处理完现有请求后退出
2.4.1 查看旧版本编译参数
平滑升级时,新版本必须和旧版本使用完全相同的编译参数,否则新 Master 进程可能因为路径、模块、启用功能不匹配而启动失败
# 查看旧版本编译参数
nginx -V
2.4.2 平滑升级示例
# 模拟用户持续访问
# 在 nginx 服务器 html/ 目录下生成测试文件
dd if=/dev/zero of=/apps/nginx/html/test.img bs=1M count=100
# 以限定速度下载测试文件,模拟用户持续连接
wget --limit-rate=1k http://10.0.0.221/test.img
# 查看当前 Nginx 版本
curl -I http://10.0.0.210
# 备份当前二进制文件
mv /usr/sbin/nginx /usr/sbin/nginx.old
# 编译新版本,注意只编译获取二进制文件,不安装
cd /usr/local/src/\
wget https://nginx.org/download/nginx-1.30.4.tar.gz
tar xf nginx-1.30.4.tar.gz
./configure \
--prefix=/usr/local/nginx \
--sbin-path=/usr/sbin/nginx \
--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 \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--add-module=/usr/local/src/echo-nginx-module
make -j$(nproc)
# 复制编译完成的二进制文件
cp objs/nginx /usr/sbin/nginx
# 用新版本二进制文件做配置文件语法检查,查看是否有报错
nginx -t
# 向旧 Master 进程发送 USR2 信号,启动新 Master 进程
kill -USR2 $(cat /var/run/nginx.pid)
# 此时如果有新请求,仍由旧版本提供服务
curl -I http://10.0.0.210
# 此时新旧 Nginx 并存
ps aux | grep nginx
# 向旧 Master 进程发送 WINCH 信号,停止旧 Worker 进程接收新连接
kill -WINCH $(cat /var/run/nginx.pid.oldbin)
# 此时如果有新请求,由新版本提供服务
curl -I http://10.0.0.221
# 此时前面模拟的用户持续连接未中断
# 此时旧 Worker 进程因仍有用户连接未退出
ps aux | grep nginx
# 取消模拟的用户持续连接
Ctrl + C
# 此时旧 Worker 进程处理完所有现有连接退出
ps aux | grep nginx
# 此时测试业务是否正常运行,没问题执行升级,否则回滚
# 升级
# 向旧 Master 进程发送 QUIT 信号,旧 Master 进程退出
kill -QUIT $(cat /var/run/nginx.pid.oldbin)
# 此时旧 Master 进程退出,只剩下新 Master 进程和 Worker 进程
ps aux | grep nginx
# 验证 Nginx 版本
nginx -v
# 回滚
# 向旧 Master 进程发送 HUP 信号,重新拉起旧版本的 worker 进程
kill -HUP `cat /apps/nginx/logs/nginx.pid.oldbin`
# 向新 Master 进程发送 QUIT 信号,退出新版本 Master 进程
kill -QUIT `cat /apps/nginx/logs/nginx.pid`
# 恢复旧版本二进制文件
mv /usr/sbin/nginx.old /usr/sbin/nginx
# 验证 Nginx 版本
nginx -v
2.5 Nginx 基础配置
主配置文件: /etc/nginx/nginx.conf
子配置文件: 通过在 http 块添加 include 指定路径,可以实现按业务创建配置文件
# 查看 Nginx 主配置文件的有效行
grep -Ev '^\s*#|^\s*$' /etc/nginx/nginx.conf
2.5.1 配置文件的层级结构
| 配置块 | 作用范围 | 职责 |
|---|---|---|
main | 全局 | Nginx 整体运行参数 (用户、Worker 进程数、PID文件) |
events | 全局 | 网络连接参数 (事件模型、最大连接数) |
http | 全局 | HTTP 通用配置 (MIME、日志、gzip) |
server | 虚拟主机 | http块的子配置块,定义网站 (域名、端口) |
location | URL 路径 | server块的子配置块,匹配 URL 并执行处理 |
配置文件中的相对路径是相对于安装时编译参数 --prefix=/usr/local/nginx 指定的路径
2.5.2 main 块配置
# 编辑主配置文件
vim /etc/nginx/nginx.conf
# 修改 Worker 进程数为 auto ,自动匹配 CPU 核心数
worker_processes auto;
# 修改后校验语法错误
nginx -t
# 重载配置
systemctl reload nginx.service
# 查看 Worker 进程数
ps aux | grep nginx
2.5.3 events 块配置
vim /etc/nginx/nginx.conf
# Nginx 默认使用 epoll 模型,添加此行只是做显性配置
use epoll;
# 修改单个 Worker 进程最大连接数
worker_connections 10240;
nginx -t
systemctl reload nginx.service
2.5.4 http 块配置
# TCP 连接存活时长,实现复用连接
keepalive_timeout 65;
# 关闭响应报文头中显示 Nginx 版本
server_tokens off;
# 指定子配置文件路径
include /etc/nginx/conf.d/*.conf;
2.5.5 server 块虚拟主机配置
每个 server 块对应一个虚拟主机,用于实现在一台物理服务器同时运行多个独立网站
每个网站相互隔离,拥有独立的域名、根目录和配置文件
2.5.5.1 虚拟主机类型
通过 listen 和 server_name 配置虚拟主机类型
| 类型 | 区分依据 | 配置指令 | 使用场景 |
|---|---|---|---|
| 基于域名 | 请求头的 Host 字段 | server_name | 同一 IP 和端口下运行多个网站 (常用) |
| 基于 IP | 请求的目标 IP 地址 | listen IP:port | 服务器绑定多个 IP,每个 IP 对应不同站点 |
| 基于端口 | 请求的目标端口号 | listen port | 同一 IP 不同端口对应不同服务,如管理后台 |
基于域名示例:
# 访问 www.test.org 走这个
server {
listen 80;
server_name www.test.org;
root /var/www/test;
}
# 访问 blog.test.com 走这个
server {
listen 80;
server_name blog.test.org;
root /var/www/blog;
}
基于 IP 示例:
# 访问 1.1.1.1 走这个
server {
listen 1.1.1.1:80;
server_name _;
root /var/www/test1;
}
# 访问 2.2.2.2 走这个
server {
listen 2.2.2.2:80;
server_name _;
root /var/www/test2;
}
基于端口示例:
# 访问 80 端口走这个(对外网站)
server {
listen 80;
server_name _;
root /var/www/test;
}
# 访问 3366 端口走这个(管理后台)
server {
listen 3366;
server_name _;
root /var/www/admin;
}
2.5.5.2 基于域名案例
# 创建网站根目录
mkdir -p /webtest/{pc,mobile,app}
# 创建测试页
echo "pc web" > /webtest/pc/index.html
echo "mobile web" > /webtest/mobile/index.html
# 编辑主配置文件在 http 块中添加子配置文件路径
vim /etc/nginx/nginx.conf
include /webtest/*.conf;
# 创建子域名 pc 配置文件
vim /webtest/pc.conf
server {
listen 80;
server_name pc.test.org;
location / {
root /webtest/pc;
index index.html;
}
}
# 创建子域名 mobile 配置文件
vim /webtest/mobile.conf
server {
listen 80;
server_name mobile.test.org;
location / {
root /webtest/mobile;
index index.html;
}
}
# 测试配置文件是否有语法错误并完整输出配置文件内容
nginx -T
# 校验语法错误
nginx -t
# 重载 Nginx 配置
systemctl reload nginx
# 测试, curl 通过 -H 携带指定请求头
curl 10.0.0.210 -H "Host: pc.test.org"
curl 10.0.0.210 -H "Host: moble.test.org"
grep -Ev "^;|^$" /etc/php-fpm.d/www.conf
2.5.6 location 块配置
2.5.6.1 location 匹配规则
location 决定了如何响应不同的请求
| 优先级 | 匹配类型 | 语法 | 示例 | 匹配后行为 |
|---|---|---|---|---|
| 1 | 精确匹配 | = /path | location = /health | 立即停止 |
| 2 | 前缀优先匹配(不区分大小写) | ^~ /path | location ^~ /static/ | 停止正则匹配 |
| 3 | 包含,正则匹配(区分大小写) | ~ /pattern | location ~ \.php$ | 按顺序匹配 |
| 4 | 包含,正则匹配(不区分大小写) | ~* /pattern | location ~* \.jpg$ | 按顺序匹配 |
| 5 | 通用前缀匹配 | /path | location /api/ | 最长匹配 |
| 6 | 通用匹配 | / | location / | 兜底 |
2.5.6.2 匹配规则示例
使用 echo 模块测试
# 创建测试配置文件
tee /etc/nginx/conf.d/location-test.conf <<'EOF'
server {
listen 80;
server_name location.deng.org;
access_log /var/log/nginx/location_test_access.log main;
error_log /var/log/nginx/location_test_error.log warn;
# 1. 精确匹配(最高优先级)
location = /health {
echo "精确匹配(=):/health";
}
# 2. 前缀优先匹配(匹配后停止正则)
location ^~ /static/ {
echo "前缀优先匹配(^~):/static/ 下的所有请求";
}
# 3. 正则匹配(区分大小写)
location ~ \.php$ {
echo "正则匹配(~):.php 结尾的请求";
}
# 4. 正则匹配(不区分大小写)
location ~* \.jpg$ {
echo "正则匹配(~*):.jpg 或 .JPG 结尾的请求";
}
# 5. 通用前缀匹配(最长匹配原则)
location /api/v1/ {
echo "通用前缀匹配:/api/v1/(更长的匹配)";
}
location /api/ {
echo "通用前缀匹配:/api/(较短的匹配)";
}
# 6. 通用匹配(兜底)
location / {
echo "通用匹配(/):所有未被匹配的请求";
}
}
EOF
vim /etc/hosts
10.0.0.210 location.deng.org
# 精确匹配配置 1
curl location.deng.org/health
# 前缀匹配配置 2
curl location.deng.org/static/111/222
# 后缀匹配配置 3
curl location.deng.org/xxx/111/222.php
# 配置 3 区分大小写,大写后缀不匹配
curl location.deng.org/xxx/111/222.PHP
# 后缀匹配配置 4 ,不区分大小写
curl location.deng.org/xxx/111/222.JPG
# 访问 /api ,匹配内容更长的
curl location.deng.org/api/v1/111
curl location.deng.org/api/111
# 不能匹配其他配置,都匹配 /
curl location.deng.org
2.5.6.3 root 与 alias
两者都用于指定文件路径,单拼接方式不同
root: root 路径 + location 路径alias: alias 路径 替换 location 路径,使用时路径末尾必须加/,否则会路径拼接错误
实验验证
# 创建测试文件
mkdir -p /var/www/static/{root-test,img}
echo "root test" > /var/www/static/root-test/test.txt
echo "alias test" > /var/www/static/img/test.txt
# 创建虚拟主机
tee /etc/nginx/conf.d/root-alias-test.conf <<'EOF'
server {
listen 80;
server_name test.deng.org;
location /root-test/ {
root /var/www/static;
}
location /alias-test/ {
alias /var/www/static/img/;
}
}
EOF
# 校验语法,成功后重载 Nginx 配置
nginx -t && systemctl reload nginx
# Windows 打开 SwitchHosts 添加一条解析记录
10.0.0.210 test.deng.org
# 浏览器访问
http://test.deng.org/root-test/test.txt
# 输出 /var/www/static/root-test/test.txt 内容
# 浏览器访问
http://test.deng.org/alias-test/test.txt
# 输出 /var/www/static/img/test.txt 内容,将 /alias-test/ 替换为 /var/www/static/img/
2.6 案例: 部署 PHP 应用 WordPress
2.6.1 整体架构
| 层级 | 组件 | 作用 |
|---|---|---|
| 接入层 | Nginx | 接收用户请求,转发 .php 请求给 PHP-FPM |
| 应用层 | PHP-FPM | 执行 WordPress 的 PHP 代码 |
| 数据层 | MySQL | 存储 WordPress 的数据 |
先参考上方编译安装部署 Nginx
2.6.2 安装 MySQL
# 安装 MySQL
dnf install -y mysql8.4-server
apt install -y mysql-server
# 启动 MySQL
systemctl enable --now mysqld
systemctl enable --now mysql
# 查看状态
systemctl status mysqld
systemctl status mysql
# 登录数据库创建数据库、用户、授权
mysql
CREATE DATABASE wordpress;
CREATE USER 'wpuser'@'localhost' IDENTIFIED BY '123456';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wpuser'@'localhost';
FLUSH PRIVILEGES;
quit
2.6.3 部署 PHP-FPM
# Rocky 10 部署
# 安装 PHP-FPM
dnf install -y php php-fpm php-mysqlnd php-gd php-xml php-mbstring
# 启动 PHP-FPM
systemctl enable --now php-fpm.service
# 查看 Socket 文件
ls /run/php-fpm/www.sock
# 查看进程
ps aux | grep php-fpm
2.6.4 下载并配置 WordPress
# 下载并解压 WordPress
wget https://cn.wordpress.org/latest-zh_CN.zip
unzip latest-zh_CN.zip
# 复制文件到网站目录
mkdir -p /var/www/wordpress
cp -r wordpress/* /var/www/wordpress
# 设置权限
chown -R nginx:nginx /var/www/wordpress
chmod -R 755 /var/www/wordpress
# 创建配置文件并配置数据库
cp /var/www/wordpress/wp-config-sample.php /var/www/wordpress/wp-config.php
vim /var/www/wordpress/wp-config.php
define( 'DB_NAME', 'wordpress' );
define( 'DB_USER', 'wpuser' );
define( 'DB_PASSWORD', '123456' );
define( 'DB_HOST', 'localhost' );
2.6.5 Nginx 配置
# 编辑主配置文件
vim /etc/nginx/nginx.conf
# 删除 # 开启日志格式定义
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 在 http 块中添加子配置文件路径
include /etc/nginx/conf.d/*.conf;
创建虚拟主机
tee /etc/nginx/conf.d/wordpress.conf <<'EOF'
server {
listen 80;
server_name wordpress.deng.org;
root /var/www/wordpress;
index index.php index.html;
access_log /var/log/nginx/wordpress_access.log main;
error_log /var/log/nginx/wordpress_error.log warn;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
log_not_found off;
access_log off;
}
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
}
EOF
nginx -t
systemctl reload nginx
2.6.6 验证并配置 WordPress
# Windows 打开 SwitchHosts 添加一条解析记录
10.0.0.210 wordpress.deng.org
浏览器访问 http://wordpress.deng.org 完成配置
2.6.7 PHP-FPM 连接方式对比
- TCP 端口 : 有网络开销,跨主机连接使用
- Socket : 无网络开销,仅限本机使用,同主机连接推荐
# PHP-FPM 配置
# 编辑配置文件
vim /etc/php-fpm.d/www.conf
# Socket 方式,默认
listen = /run/php-fpm/www.sock
# 端口方式
listen = 9000
# 端口模式,开启此配置项表示只允许本机连接,不开启则任何主机都可以连接
listen.allowed_clients = 127.0.0.1
# Nginx 配置
# Socket 方式
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
include fastcgi_params;
}
# 端口方式
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
}
2.6.8 PHP-FPM 进程管理
| 模式 | 说明 | 使用场景 |
|---|---|---|
static | 固定子进程数,启动时创建 pm.max_children 配置项指定个子进程 | 高并发、内存充足 |
dynamic | 动态调整,根据负载在 pm.min_spare_servers 和 pm.max_children 之间调整 | 大多数场景(默认) |
ondemand | 按需创建,空闲进程在 pm.process_idle_timeout 指定时间后退出 | 低并发、内存敏感 |
# 编辑配置文件
vim /etc/php-fpm.d/www.conf
# 必须项,指定管理模式
pm = dynamic
# static 模式配置
pm.max_children = 50 ; 固定50个子进程
# dynamic 模式配置
pm.max_children = 50 ; 最大子进程数
pm.start_servers = 5 ; 启动时创建子进程数
pm.min_spare_servers = 5 ; 最少空闲时子进程数
pm.max_spare_servers = 35 ; 最大空闲时子进程数
# ondemand 模式配置
pm.max_children = 50
pm.process_idle_timeout = 10s ; 空闲 10 秒后退出
# 查看 PHP-FPM 进程数
ps aux | grep php-fpm | grep -v grep | wc -l
# 进程数计算
pm.max_children = (总内存 - 系统占用内存) / 单个 PHP-FPM 进程占用内存
# 示例,服务器 16GB 内存
系统占用 2GB
单个 PHP-FPM 进程占用内存 70 - 90 MB
(16GB - 2GB) / 80MB ≈ 180
保守建议: pm.max_children = 150
2.6.9 PHP-FPM 状态页
# 编辑配置文件,开启状态页
vim /etc/php-fpm.d/www.conf
pm.status_path = /status
# 重载 PHP-FPM 配置
systemctl reload php-fpm
# 编辑虚拟主机配置
vim /etc/nginx/conf.d/wordpress.conf
# 添加 状态页
location = /status {
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 重载 Nginx 配置
systemctl reload nginx
# 测试访问状态页
curl http://127.0.0.1/status -H "Host: wordpress.deng.org"
2.7 案例: 部署 Python 应用
# Rocky 10
# 安装 python 和 pip
dnf install -y python3 python3-pip
# 安装 gunicorn 和 flask
pip3 install gunicorn flask
# 创建应用目录
mkdir -p /var/www/flask-app
# 创建 flask 应用
tee /var/www/flask-app/app.py <<'EOF'
#!/usr/bin/env python3
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/')
def hello():
return jsonify({
'message': 'Hello from Flask!',
'status': 'success'
})
@app.route('/api/user/<int:uid>')
def get_user(uid):
return jsonify({
'uid': uid,
'name': f'User_{uid}',
'email': f'user{uid}@qq.com'
})
@app.route('/api/health')
def health():
return jsonify({'status': 'ok'})
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8000)
EOF
# 启动 gunicorn , 指定工作进程数、绑定地址和端口、模块名和实例名、运行方式为后台运行
cd /var/www/flask-app ; gunicorn -w 4 -b 127.0.0.1:8000 app:app --daemon
# 验证 gunicorn 是否启动
ss -ntlp | grep 8000
# Nginx 配置
# 编辑主配置文件
vim /etc/nginx/nginx.conf
# 删除 # 开启日志格式定义
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 在 http 块中添加子配置文件路径
include /etc/nginx/conf.d/*.conf;
# 配置 Nginx 代理 Python 应用
tee /etc/nginx/conf.d/flask.conf <<'EOF'
server {
listen 80;
server_name flask.deng.org;
access_log /var/log/nginx/flask_access.log main;
error_log /var/log/nginx/flask_error.log warn;
location / {
proxy_pass http://127.0.0.1:8000;
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 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
location /api/ {
proxy_pass http://127.0.0.1:8000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
EOF
# 校验语法,成功后重载 Nginx 配置
nginx -t && systemctl reload nginx
# Windows 打开 SwitchHosts 添加一条解析记录
10.0.0.210 flask.deng.org
# 浏览器访问验证,正常输出 JSON 数据
http://flask.deng.org
http://flask.deng.org/api/user/100
三、Nginx 反向代理和 LB
3.1 相关概念
3.1.1 正向代理 vs 反向代理
| 对比纬度 | 正向代理 | 反向代理 |
|---|---|---|
| 代理对象 | 客户端 | 服务端 |
| 配置端 | 客户端 | 服务端 |
| 隐藏IP | 客户端真实 IP | 服务端真实 IP |
| 使用场景 | 公司内网通过代理访问外网 | 流量入口将请求转发给后端应用 |
3.1.2 反向代理的核心价值
| 问题 | Nginx 的解决方案 | 价值 |
|---|---|---|
| 后端直接暴露不安全 | 后端只监听在内网或本机,Nginx 做唯一入口 | 安全 |
| 多个应用需要统一入口 | 根据域名/路径转发到不同后端 | 统一管理 |
| SSL 证书需要在每个应用配置 | SSL 在 Nginx 部署 | 简化运维 |
| 静态请求和动态请求混合 | 静态请求由 Nginx 处理直接返回 | 性能优化 |
| 单点故障 | 后端多实例,Nginx 做负载均衡 | 高可用 |
3.1.3 负载均衡 (LB) 的核心价值
| 价值 | 说明 |
|---|---|
| 高可用 | 一台后端宕机,自动切换其他后端 (健康检查) |
| 高并发 | 多台后端分担请求,整体处理能力提升 |
| 弹性扩展 | 可动态扩缩容后端节点 |
| 灰度发布 | 部分后端运行新版,逐步切流 |
3.2 测试环境准备
3.2.1 节点初始化
# 初始化环境
# 修改各节点主机名
hostnamectl set-hostname lb1
hostnamectl set-hostname backend1
hostnamectl set-hostname backend2
hostnamectl set-hostname backend3
# 添加本地解析记录
vim /etc/hosts
10.0.0.210 lb1
10.0.0.211 backend1
10.0.0.212 backend2
10.0.0.213 backend3
3.2.2 Nginx 初始化
# 编辑主配置文件
vim /etc/nginx/nginx.conf
# 删除 # 开启日志格式定义
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 在 http 块中添加子配置文件路径
include /etc/nginx/conf.d/*.conf;
# 创建子配置文件目录
mkdir /etc/nginx/conf.d
3.2.3 模拟后端服务
# backend 1
# 创建测试页
mkdir -p /var/www/backend/api
echo "Backend 1 (10.0.0.211)" > /var/www/backend/index.html
echo '{"code":200,"data":"Backend 1"}' > /var/www/backend/api/user
# 配置 Nginx 虚拟主机监听 8080 端口
tee /etc/nginx/conf.d/backend.conf <<'EOF'
server {
listen 8080;
server_name backend;
root /var/www/backend;
index index.html;
access_log /var/log/nginx/backend_access.log main;
error_log /var/log/nginx/backend_error.log warn;
}
EOF
# 校验语法成功后重载配置
nginx -t && systemctl reload nginx
# backend 2 和 backend 3 仅测试页数据不同,其他步骤一致
echo "Backend 2 (10.0.0.212)" > /var/www/backend/index.html
echo '{"code":200,"data":"Backend 2"}' > /var/www/backend/api/user
echo "Backend 3 (10.0.0.213)" > /var/www/backend/index.html
echo '{"code":200,"data":"Backend 3"}' > /var/www/backend/api/user
3.2.4 LB 静态资源
mkdir -p /var/www/nginx
echo "LB Home Page" > /var/www/nginx/index.html
3.3 示例一: 反向代理单后端
示例配置 后端应用 获取的客户端请求 IP 都是 LB
# LB 创建反向代理 虚拟主机 配置
tee /etc/nginx/conf.d/proxy-single.conf <<'EOF'
server {
listen 80;
server_name proxy.deng.org;
location / {
proxy_pass http://backend1:8080;
}
}
EOF
# 校验语法成功后重载配置
nginx -t && systemctl reload nginx
# 验证
# 命令行验证
curl 10.0.0.210 -H "Host: proxy.deng.org"
# Windows 打开 SwitchHosts 添加一条解析记录
10.0.0.210 proxy.deng.org
# 浏览器访问
http://proxy.deng.org/
http://proxy.deng.org/api/user
# backend 1 查看日志,发现访问客户端都是 LB
cat /var/log/nginx/backend_access.log
3.4 示例二: 透传 客户端信息
通过添加 透传指定头部信息 将 真实客户端信息 传递给后端服务
# 配置项说明
# 在 location 块添加
# 原始 Host
proxy_set_header Host $host;
# 客户端真实 IP
proxy_set_header X-Real-IP $remote_addr;
# 代理链
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 原始协议(http/https)
proxy_set_header X-Forwarded-Proto $scheme;
# LB 虚拟主机配置 添加 透传配置项
server {
listen 80;
server_name proxy.deng.org;
location / {
proxy_pass http://backend1: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;
}
}
# 校验语法成功后重载配置
nginx -t && systemctl reload nginx
# 验证
# 浏览器访问
http://proxy.deng.org/
# backend 1 查看日志,每条日志末尾显示真实客户端 IP (对应日志格式中 http_x_forwarded_for 变量)
cat /var/log/nginx/backend_access.log
3.5 示例三: 负载均衡
# upstream 用于定义一组后端服务器
# 格式
upstream 组名 {
server 后端地址1 [参数];
server 后端地址2 [参数];
server 后端地址3 [参数];
}
# LB 配置负载均衡,使用默认调度策略 轮询
tee /etc/nginx/conf.d/proxy-lb.conf <<'EOF'
upstream backend {
server backend1:8080;
server backend2:8080;
server backend3:8080;
}
server {
listen 80;
server_name lb.deng.org;
# 静态资源:LB 直接返回
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|css|js)$ {
expires 30d;
access_log off;
}
# API 请求:转发到后端(负载均衡)
location /api/ {
proxy_pass http://backend;
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;
}
# 其他请求:LB 直接返回静态文件,按顺序在磁盘查找文件,全部找不到执行最后一项
location / {
root /var/www/nginx;
try_files $uri $uri/ /index.html;
}
}
EOF
# 校验语法成功后重载配置
nginx -t && systemctl reload nginx
# 命令行测试
vim /etc/hosts
10.0.0.210 lb.deng.org
# 单次测试
curl -s http://lb.deng.org/api/user
# 循环测试
for i in {1..15};do curl -s http://lb.deng.org/api/user;done
# backend 查看日志,因为 轮询 策略的缘故,每台后端承担 1/后端节点总数 的请求
cat /var/log/nginx/backend_access.log
proxy_pass 后参数末尾带 / 会去掉 location 匹配的路径部分,不带 / 则原样转发
3.6 负载均衡调度算法
常见四种算法
| 算法 | 配置 | 适用场景 |
|---|---|---|
| 轮询 (round-robin) | 默认 | 后端性能均等 |
| 权重 (weight) | weight=N | 后端性能不均 |
| ip_hash | ip_hash; | 会话保存 (本地 Session) |
| least_conn | least_conn; | 长连接场景 (WebSocke、SSE) |
3.6.1 权重算法示例
# LB 修改负载均衡配置,在后端服务器定义给高性能后端节点配置高权重
vim /etc/nginx/conf.d/proxy-lb.conf
server backend1:8080 weight=3;
server backend2:8080 weight=1;
server backend3:8080 weight=1;
nginx -t && systemctl reload nginx
# 追踪后端节点日志
tail -f /var/log/nginx/backend_access.log
# 循环测试,查看调度策略
for i in {1..15};do curl -s http://lb.deng.org/api/user;done
# 结果符合权重配置 3:1:1 比例
3.6.2 ip_hash 算法示例
基于的客户端的 remote_addr (源地址IPv4前24位或整个IPv6地址) 做 hash 计算
# LB 修改负载均衡配置,在后端服务器定义添加 ip_hash;
vim /etc/nginx/conf.d/proxy-lb.conf
upstream backend {
server backend1:8080;
server backend2:8080;
server backend3:8080;
ip_hash;
}
nginx -t && systemctl reload nginx
# 循环测试,查看调度策略
for i in {1..15};do curl -s http://lb.deng.org/api/user;done
# 因为客户端 IP 相同,所有请求都被调度到同一个节点
3.7 被动健康检查
# LB 修改负载均衡配置,在后端服务器定义添加 max_fails=3 fail_timeout=30s
vim /etc/nginx/conf.d/proxy-lb.conf
upstream backend {
server backend1:8080 max_fails=3 fail_timeout=30s;
server backend2:8080 max_fails=3 fail_timeout=30s;
server backend3:8080 max_fails=3 fail_timeout=30s;
}
nginx -t && systemctl reload nginx
# 停止一个后端服务,模拟后端服务宕机
systemctl stop nginx
# 循环测试,查看调度策略
for i in {1..15};do curl -s http://lb.deng.org/api/user;done
# 不再往宕机后端服务转发
# LB 查看错误日志,出现 3 次失败转发
tail /var/log/nginx/error.log
# 启动后端服务,模拟故障恢复
systemctl start nginx
for i in {1..15};do curl -s http://lb.deng.org/api/user;done
# 恢复故障后端转发
参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
max_fails | 1 | 失败指定次数后将对应后端服务器标记为不可用 |
fail_timeout | 10s | 后端服务器被标记为不可用后,等待指定时间再次尝试转发请求,成功则恢复后端,失败则等待指定时间再次尝试 |
3.8 四层代理
Nginx 支持 四层代理,但一般不使用 Nginx 做四层代理
需要编译启用 stream 模块
四、HTTPS 与 安全加固
4.1 HTTPS 配置
在 LB 配置
4.1.1 确认 SSL 模块已安装
nginx -V 2>&1 | grep with-http_ssl_module
4.1.2 公网 HTTPS 案例
4.1.2.1 准备域名
dengtest.pw
4.1.2.2 购买云服务
阿里云 - 香港 -抢占式实例
Dengtest123
4.1.2.3 DNS解析

4.1.2.4 源码部署 Nginx 及基础配置
源码部署 Nginx
# 安装依赖包
dnf install -y curl wget vim net-tools tar git gcc make pcre2-devel zlib-devel openssl-devel epel-release
# 下载源码包并解压
cd /usr/local/src/
wget https://nginx.org/download/nginx-1.30.4.tar.gz
git clone https://github.com/openresty/echo-nginx-module.git
tar xf nginx-1.30.4.tar.gz
# 创建 nginx 用户
useradd -s /sbin/nologin -r nginx
# 编译并安装
cd nginx-1.30.4/
./configure --prefix=/usr/local/nginx --sbin-path=/usr/sbin/nginx --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 --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --add-module=/usr/local/src/echo-nginx-module
make -j$(nproc)
make install
# 创建服务文件
tee /etc/systemd/system/nginx.service <<'EOF'
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target
[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
LimitNOFILE=100000
[Install]
WantedBy=multi-user.target
EOF
# 启动 nginx
systemctl daemon-reload && systemctl enable --now nginx
Nginx 基础配置
# 编辑主配置文件
vim /etc/nginx/nginx.conf
# 删除 # 开启日志格式定义
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 在 http 块中添加子配置文件路径
include /etc/nginx/conf.d/*.conf;
# 创建子配置文件目录
mkdir /etc/nginx/conf.d
4.1.2.5 创建虚拟主机
# 创建测试页
mkdir -p /var/www/nginx
echo "HTTPS Test..." > /var/www/nginx/index.html
# 创建虚拟主机配置
tee /etc/nginx/conf.d/proxy-lb.conf <<'EOF'
server {
listen 80;
server_name www.dengtest.pw;
access_log /var/log/nginx/dengtest_access.log main;
error_log /var/log/nginx/dengtest_error.log warn;
location / {
root /var/www/nginx;
try_files $uri $uri/ /index.html;
}
}
EOF
4.1.2.6 Let’s Encrypt 申请证书并自动部署
# 安装 Let's Encrypt 证书申请和自动部署软件包
dnf install -y certbot python3-certbot-nginx
# 申请证书并自动部署
certbot --nginx -d dengtest.pw
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address or hit Enter to skip.
(Enter 'c' to cancel): # 按 回车 跳过
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at:
https://letsencrypt.org/documents/LE-SA-v1.8-July-06-2026.pdf
You must agree in order to register with the ACME server. Do you agree?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y # 按 Y 同意协议
Account registered.
Requesting a certificate for dengtest.pw
Successfully received certificate. # 完成挑战,成功接收证书 fullchain.pem 和私钥 privkey.pem
Certificate is saved at: /etc/letsencrypt/live/dengtest.pw/fullchain.pem
Key is saved at: /etc/letsencrypt/live/dengtest.pw/privkey.pem
This certificate expires on 2026-11-02.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate # 自动完成证书部署
Successfully deployed certificate for dengtest.pw to /etc/nginx/conf.d/proxy-lb.conf
Congratulations! You have successfully enabled HTTPS on https://dengtest.pw
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
4.1.2.7 虚拟主机配置修改
# 修改自动生成的虚拟主机配置项,实现访问 dengtest.pw 和 www.dengtest.pw 均自动跳转到 https://www.dengtest.pw
vim /etc/nginx/conf.d/proxy-lb.conf
server {
server_name www.dengtest.pw;
access_log /var/log/nginx/dengtest_access.log main;
error_log /var/log/nginx/dengtest_error.log warn;
location / {
root /var/www/nginx;
try_files $uri $uri/ /index.html;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/dengtest.pw/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/dengtest.pw/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
listen 80;
server_name dengtest.pw www.dengtest.pw;
return 301 https://www.dengtest.pw$request_uri;
}
# 检测语法,成功后重载 Nginx 配置
nginx -t && systemctl reload nginx
# 查看 Nginx 是否开启 443 端口监听
ss -ntlp
4.1.2.8 测试
浏览器访问:
dengtest.pw
www.dengtest.pw
4.1.3 内网 HTTPS 案例
基于 3.5 已部署 Nginx 负载均衡集群
4.1.3.1 生成泛域名证书
# 在 LB 节点操作
# 安装软件包
dnf install -y openssl
# 创建证书存放目录
mkdir -p /opt/ca && cd /opt/ca
# 生成根 CA
openssl req -x509 -new -nodes -newkey rsa:4096 -keyout ca.key -out ca.crt \
-days 3650 -sha256 -subj "/C=CN/ST=Shanghai/L=Shanghai/O=WangXin/OU=IT/CN=WangXin Root CA"
chmod 400 ca.key
# 为项目生成私钥 key 和证书申请文件 csr
openssl req -new -nodes -out deng.csr -keyout deng.key \
-subj "/C=CN/ST=Shanghai/L=Shanghai/O=WangXin/OU=IT/CN=*.deng.org" \
-addext "subjectAltName=DNS:*.deng.org,DNS:deng.org"
# 用私有 CA 签发证书
openssl x509 -req -in deng.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out deng.crt -days 365 -copy_extensions copy
# 验证 SAN 是否生效
openssl x509 -in deng.crt -text -noout | grep -A 3 "Subject Alternative Name"
4.1.3.2 Nginx 使用证书
# LB 配置虚拟主机 HTTPS
vim /etc/nginx/conf.d/proxy-lb.conf
upstream backend {
server backend1:8080 max_fails=3 fail_timeout=30s;
server backend2:8080 max_fails=3 fail_timeout=30s;
server backend3:8080 max_fails=3 fail_timeout=30s;
}
server {
listen 443 ssl;
server_name www.deng.org;
ssl_certificate /opt/ca/deng.crt;
ssl_certificate_key /opt/ca/deng.key;
# 静态资源:LB 直接返回
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|css|js)$ {
expires 30d;
access_log off;
}
# API 请求:转发到后端(负载均衡)
location /api/ {
proxy_pass http://backend;
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;
}
# 其他请求:LB 直接返回静态文件,按顺序在磁盘查找文件,全部找不到执行最后一项
location / {
root /var/www/nginx;
try_files $uri $uri/ /index.html;
}
}
server {
listen 80;
server_name www.deng.org deng.org;
return 301 https://www.deng.org$request_uri;
}
# 检测语法,成功后重载 Nginx 配置
nginx -t && systemctl reload nginx
4.1.3.3 Windows 访问
导入私有 CA 证书文件 ca.crt










4.1.3.4 Linux 访问
# Rocky 复制私有 CA 证书到证书信任目录并更新证书信任
cp ca.crt /etc/pki/ca-trust/source/anchors/ && update-ca-trust
# Ubuntu 复制私有 CA 证书到证书信任目录并更新证书信任
cp ca.crt /usr/local/share/ca-certificates/ && update-ca-certificates
# 添加本地 hosts 解析记录指向 LB
vim /etc/hosts
10.0.0.210 www.deng.org deng.org
# 跟随跳转测试访问
curl -L deng.org
4.2 安全加固
4.2.1 隐藏版本号
# 在 http 块中添加 server_tokens off; 配置项,隐藏版本号
vim /etc/nginx/nginx.conf
http {
server_tokens off;
}
# 测试访问,首部 Server 头隐藏 Nginx 版本号
curl -I lb.deng.org
Server: nginx
4.2.2 限制 HTTP 危险方法
除非对网站非常了解,明确禁止某些方法,否则不要使用
# 在 server 块添加
server {
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
}
4.2.3 禁止目录浏览
# 新版 Nginx 默认关闭目录浏览
autoindex off;
# 如果需要开启指定目录浏览,在 location 块配置
location /iso {
autoindex off;
root /var/www/nginx;
}
4.4 防盗链
白名单机制
一般在 location 块给静态资源(图片,视频等媒体资源)配置防盗链,以节省流量
4.4.1 valid_referers 参数详解
| 参数 | 说明 | 使用场景 |
|---|---|---|
none | 没有 Referer 的请求 | 地址栏输入、书签打开 |
blocked | Referer 被防火墙/代理移除的请求 | 企业防火墙、隐私插件 |
server_names | Referer 匹配 server_name 列表 | 本站页面发起 |
ruoyi.deng.org | 明确指定域名 | 从指定域名发起 |
*.deng.org | 通配符匹配子域名 | 从指定泛域名发起 |
4.4.2 配置示例
vim /etc/nginx/conf.d/proxy-lb.conf
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|css|js)$ {
valid_referers none blocked server_names ruoyi.deng.org *.deng.org; # 配置白名单
if ($invalid_referer) { # 非白名单请求返回 403 状态码
return 403;
}
expires 30d;
access_log off;
}
# 创建测试文件
mkdir /var/www/nginx/image
echo "Test Image ..." > /var/www/nginx/image/pthot.jpg
# 测试
# 正常访问
curl lb.deng.org/image/pthot.jpg
# 携带未在白名单中 Referer 请求头测试访问
curl -H "Referer: http://www.xxx.com" lb.deng.org/image/pthot.jpg
# 拒绝访问,返回 403
# 通配域名,允许访问
curl -H "Referer: http://xxx.deng.org" lb.deng.org/image/pthot.jpg
4.5 限流配置
限制客户端单位时间的请求数量,防止恶意攻击
在 http 块定义限流区域和超出容量返回规则,在动态资源和敏感请求(例如登录) 的 location 块配置限流
# 配置示例
vim /etc/nginx/conf.d/proxy-lb.conf
# 定义限流区域,$binary_remote_addr表示限制每个客户端IP,zone=api:10m表示限流区域名称和内存缓冲池大小,rate=10r/s表示每 100ms 释放 1 个配额
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
upstream backend {
server backend1:8080 max_fails=3 fail_timeout=30s;
server backend2:8080 max_fails=3 fail_timeout=30s;
server backend3:8080 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name lb.deng.org;
root /var/www/nginx;
# 自定义超出容量返回 429 响应
error_page 503 = @rate_limit;
location @rate_limit {
add_header Content-Type application/json;
return 429 '{"code":429,"message":"请求过于频繁"}';
}
# 静态资源:LB 直接返回
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|css|js)$ {
valid_referers none blocked server_names ruoyi.deng.org *.deng.org;
if ($invalid_referer) {
return 403;
}
expires 30d;
access_log off;
}
# 登录接口,瞬间最多允许 3 个请求,后续每秒允许 1 次(防暴力破解)
location /api/login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://backend/api/login;
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;
}
# 普通 API 请求,瞬间最多允许 20 个请求,后续 100ms 允许 1 次
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
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;
}
# 其他请求:LB 直接返回静态文件
location / {
try_files $uri $uri/ /index.html;
}
}
nginx -t && systemctl reload nginx
# 测试访问普通 API,-s 静默模式(不显示进度条和统计信息),-o /dev/null 丢弃响应内容(只看状态码),-w "%{http_code}\n" 输出 HTTP 状态码
for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" http://lb.deng.org/api/user ;done | sort | uniq -c
24 200
26 429
# 测试访问登录 API
for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" http://lb.deng.org/api/login ;done | sort | uniq -c
4 404
46 429
4.6 综合配置案例
# 主配置文件 /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
server_tokens off;
events {
worker_connections 10240;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# ===== 限流定义 =====
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
# ===== 自定义 429 响应 =====
error_page 503 = @rate_limit;
location @rate_limit {
add_header Content-Type application/json;
return 429 '{"code":429,"message":"请求过于频繁"}';
}
# ===== 日志格式 =====
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# ===== 禁止目录浏览 =====
autoindex off;
include /etc/nginx/conf.d/*.conf;
}
# 子配置文件 /etc/nginx/conf.d/ruoyi-https.conf
# ===== 定义后端服务 =====
upstream ruoyi_backend {
server ruoyi_backend1:8080;
server ruoyi_backend2:8080;
server ruoyi_backend3:8080;
}
server {
listen 443 ssl http2;
server_name ruoyi.deng.org deng.org;
# ===== 私有 CA 证书 =====
ssl_certificate /opt/ca/certs/deng.org/deng.crt;
ssl_certificate_key /opt/ca/certs/deng.org/deng.key;
# ===== TLS 优化 =====
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# ===== 安全响应头 =====
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
server_tokens off;
# ===== 防盗链:静态资源由 LB 直接返回 =====
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|css|js)$ {
root /var/www/nginx;
valid_referers none blocked server_names ruoyi.deng.org deng.org *.deng.org;
if ($invalid_referer) {
return 403;
}
expires 30d;
access_log off;
}
# ===== 登录接口限流 =====
location /api/login {
limit_req zone=login burst=3 nodelay;
proxy_pass http://ruoyi_backend/api/login;
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;
}
# ===== API 请求限流 =====
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://ruoyi_backend;
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;
}
# ===== 其他请求:LB 直接返回 =====
location / {
root /var/www/nginx;
try_files $uri $uri/ /index.html;
}
}
# ===== HTTP 跳转 HTTPS =====
server {
listen 80;
server_name ruoyi.deng.org deng.org;
return 301 https://$host$request_uri;
}
五、日志管理
5.1 日志类型
- 访问日志: 默认文件位置 /var/log/nginx/access.log , 记录每次请求的详细信息
- 错误日志: 默认文件位置 /var/log/nginx/error.log , 记录 Nginx 运行中的错误和警告信息
5.2 访问日志格式
vim /etc/nginx/nginx.conf
http {
# Nginx 默认访问日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
}
# 完整日志格式,含性能统计
log_format detailed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'urt="$upstream_response_time" uaddr="$upstream_addr"';
5.3 访问日志变量解析
| 变量 | 说明 | 输出示例 | 排障价值 |
|---|---|---|---|
$remote_addr | 客户端 IP | 10.0.0.240 | 定位访问来源 |
$remote_user | 认证用户名 | admin | 审计用户行为 |
$time_local | 本地时间 | 05/Aug/2026:13:10:22 +0800 | 确定请求时间 |
$request | 完整请求行 | GET /api/login HTTP/1.1 | 查看具体请求 |
$status | HTTP 状态码 | 200 | 判断请求是否成功 |
$body_bytes_sent | 响应体大小 | 32 | 流量统计 |
$http_referer | 来源页面 | http://bl.deng.org/ | 来源分析/防盗链 |
$http_user_agent | 用户代理信息 | Mozilla/5.0 … | 客户端统计 |
$http_x_forwarded_for | 客户端真实 IP 链 | 10.0.0.240 | 代理场景溯源 |
$request_time | 请求总耗时,单位秒 | 0.002 | 性能分析 |
$upstream_addr | 后端地址 | 10.0.0.211:8080 | 确认请求转发 |
$upstream_response_time | 后端响应时间 | 0.001 | 后端性能分析 |
$upstream_connect_time | 连接后端耗时 | 0.000 | 网络问题排查 |
5.4 错误日志级别
| 级别 | 值 | 说明 | 生产环境配置 |
|---|---|---|---|
debug | 1 | 调试信息 | 不推荐 |
info | 2 | 一般信息 | 不推荐 |
notice | 3 | 正常但重要 | 不推荐 |
warn | 4 | 警告 | 可用 |
error | 5 | 错误 | 推荐 |
crit | 6 | 严重错误 | 推荐 |
alert | 7 | 需要立即处理 | 推荐 |
emerg | 8 | 系统不可用 | 推荐 |
配置建议
# 主配置文件
error_log /var/log/nginx/error.log error;
# 开发调试
error_log /var/log/nginx/error.log debug;
# 虚拟主机独立日志
error_log /var/log/nginx/ruoyi_error.log warn;
常见错误日志解析
| 错误信息 | 含义 | 解决方案 |
|---|---|---|
permission denied | 文件权限不足 | chmod 755 或 chown nginx:nginx |
connect() failed (111: Connection refused) | 后端服务未启动 | 启动后端服务 |
connect() failed (113: No route to host) | 网络不通 | 检查防火墙/路由 |
SSL_CTX_use_PrivateKey_file failed | 证书私钥不匹配 | 查看证书和私钥是否匹配 |
upstream timed out | 后端响应超时 | 增加 proxy_read_timeout |
client intended to send too large body | 请求头过大 | 增加 client_max_body_size |
5.5 配置日志示例
# 主配置文件定义日志格式
vim /etc/nginx/nginx.conf
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format detailed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'urt="$upstream_response_time" uaddr="$upstream_addr"';
access_log /var/log/nginx/access.log main;
}
# 项目配置独立日志
vim /etc/nginx/conf.d/proxy-lb.conf
server {
access_log /var/log/nginx/ruoyi_access.log detailed;
error_log /var/log/nginx/ruoyi_error.log warn;
}
nginx -t && systemctl reload nginx
# 测试访问后验证日志输出
tail /var/log/nginx/ruoyi_access.log
5.6 JSON 格式日志
# 主配置文件定义日志格式
vim /etc/nginx/nginx.conf
http {
log_format json escape=json '{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"remote_user":"$remote_user",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for",'
'"upstream_addr":"$upstream_addr",'
'"upstream_status":"$upstream_status",'
'"upstream_response_time":"$upstream_response_time"'
'}';
access_log /var/log/nginx/access.json.log json;
}
5.7 logrotate 日志切割
在 logrotate 配置文件定义轮转规则,logrotate 按规则定期对日志进行切割
配置项解析
| 配置项 | 说明 |
|---|---|
daily / weekly | 每天切割 /每周切割 |
missingok | 文件缺失不报错 |
rotate 30 | 旧日志保留副本数 |
compress | 压缩旧日志 |
delaycompress | 延迟一天压缩 |
dateext | 旧日志文件添加日期后缀 |
notifempty | 空文件不切割 |
sharedscripts | 对多个日志做切割时,所有日志切割后执行一次脚本 |
postrotate | 声明脚本起始 |
endscript | 声明脚本结束 |
# 创建 nginx 日志切割规则
vim /etc/logrotate.d/nginx
# 对 /var/log/nginx/ 目录下所有 .log 结尾文件进行切割
/var/log/nginx/*.log {
daily
missingok
rotate 30
dateext
compress
delaycompress
notifempty
sharedscripts
postrotate
/usr/bin/killall -HUP nginx
endscript
}
# 查看 logrotate 定时任务
systemctl list-timers
Thu 2026-08-06 00:10:24 CST 8h Wed 2026-08-05 10:02:36 CST 5h 43min ago logrotate.timer logrotate.service
# 手动触发 logrotate
logrotate -f /etc/logrotate.d/nginx
# 查看切割后的日志
ll /var/log/nginx/
六、性能调优
面向资源(CPU计算、内存、存储、网络)优化
内存优化 : 对其他资源优化时,要预留好内存资源,避免耗尽内存资源
CPU 优化 : Worker 进程数、Worker 进程和 CPU 核心绑定、多路复用、增大 Worker 进程优先级(设置为 -10)、避免第三方模块携带阻塞操作和长时间 CPU 密集计算
网络优化 : Nginx作为客户端(upstream 长连接、增加本地端口可用范围、前后端超时对齐)、Nginx作为服务端(半连接队列长度、全连接队列长度、临时启用 syncookie)、提升句柄数量上限(文件描述符限制)
存储优化 : 文件缓存、浏览器缓存、压缩、大文件直接 I/O、异步 I/O、线程池
6.1 Worker 进程优化
6.1.1 Worker 进程数
进程数应等于核心数而不是线程数,超线程会造成调度争抢
vim /etc/nginx/nginx.conf
# 全局配置
# Worker 进程数自动检测
worker_processes auto;
nginx -t && systemctl reload nginx
ps aux | grep nginx
6.1.2 worker_cpu_affinity 进程和核心绑定
vim /etc/nginx/nginx.conf
# 全局配置
worker_cpu_affinity 00000001 00000010;
nginx -t && systemctl reload nginx
# 验证绑定关系
ps -eo pid,cmd,psr | grep nginx
6.1.3 文件描述符上限
# Nginx 四层限制链路(生效取最小值)
内核全局 fs.file-max / fs.nr_open
↓
systemd LimitNOFILE(master进程继承硬上限)
↓
nginx.conf worker_rlimit_nofile(worker进程能上调的上限)
↓
events { worker_connections }
vim /etc/nginx/nginx.conf
# 全局配置
worker_rlimit_nofile 65535;
nginx -t && systemctl reload nginx
# nginx 直接启动修改系统文件描述符上限
# 临时修改
ulimit -n 65535
# 永久修改
echo "* soft nofile 65535" >> /etc/security/limits.conf
echo "* hard nofile 65535" >> /etc/security/limits.conf
# 重新登录查看系统文件描述符上限
ulimit -n
# systemd 启动修改系统文件描述符上限
nginx.service 文件
[Service]
LimitNOFILE=65535
6.1.4 单个 Worker 最大连接数
events {
worker_connections 65535;
}
nginx -t && systemctl reload nginx
6.1.5 多路复用
multi_accept on # 开启多路复用
http {
server {
listen 80 reuseport; # 关键配置
}
}
6.2 网络优化
6.2.1 sendfile 零拷贝
vim /etc/nginx/nginx.conf
http {
sendfile on; # 启用零拷贝,新版默认启用
tcp_nopush on; # 配合 sendfile ,小包累积再发送;默认注释,去除 # 启用
}
nginx -t && systemctl reload nginx
6.2.2 keepalive 长连接优化
复用 TCP 连接
vim /etc/nginx/nginx.conf
http {
keepalive_timeout 65; # 新版默认启用,空闲长连接超时时间
keepalive_requests 10000; # 单个连接最大请求数
}
6.2.3 upstream 长连接
LB 配置
vim /etc/nginx/conf.d/proxy-lb.conf
upstream backend {
server backend1:8080 max_fails=3 fail_timeout=30s;
server backend2:8080 max_fails=3 fail_timeout=30s;
server backend3:8080 max_fails=3 fail_timeout=30s;
keepalive 32; # 维持的空闲长连接最大数量
keepalive_requests 1000; # 单个连接最大请求数
keepalive_timeout 60s; # 空闲长连接超时时间
}
server {
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://backend;
proxy_http_version 1.1; # HTTP/1.1 默认开启长连接
proxy_set_header Connection ""; # 清空转发给后端的 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;
}
}
nginx -t && systemctl reload nginx
6.2.4 全连接队列长度
需配合内核参数优化项 net.core.somaxconn,两者取最小者生效
server {
listen 80 backlog=4096; # 重点:backlog 指定监听队列长度
server_name localhost;
# ...
}
6.3 Web Server 优化
6.3.1 open_file_cache 文件缓存
vim /etc/nginx/nginx.conf
http {
open_file_cache max=10000 inactive=60s; # 最多缓存 10000 个文件,60 秒未被访问则淘汰
open_file_cache_valid 60s; # 缓存有效期 60 秒
open_file_cache_min_uses 2; # 60秒内被访问 2 次才缓存
open_file_cache_errors on; # 文件不存在的错误信息也缓存
}
6.3.2 expires 浏览器缓存过期时间
vim /etc/nginx/conf.d/proxy-lb.conf
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|css|js)$ {
valid_referers none blocked server_names ruoyi.deng.org *.deng.org;
if ($invalid_referer) {
return 403;
}
expires 30d; # 浏览器缓存 30 天
access_log off;
}
| 参数 | 含义 | 适用场景 |
|---|---|---|
expires 30d | 缓存 30 天 | 图片、字体 (永久不变化) |
expires 7d | 缓存 7 天 | CSS / JS (版本更新变化) |
expires 1h | 缓存 1 小时 | 热点数据、API 响应 |
expires -1 | 不缓存 | HTML (需实时更新) |
expires epoch | 不缓存 | |
expires max | 最大缓存 (10年) | 完全静态资源 |
6.3.3gzip 压缩
压缩传输内容,减少带宽占用,但会消耗 CPU 资源
vim /etc/nginx/nginx.conf
http {
gzip on; # 开启 gzip 响应压缩
gzip_min_length 1k; # 最小压缩阈值
gzip_comp_level 4; # 压缩等级, 4 属于均衡选择
gzip_types text/plain text/css text/xml application/json # 指定压缩的 MIME 类型
application/javascript application/xml text/javascript;
gzip_vary on; # 自动增加响应头
gzip_disable "MSIE [1-6]\."; # 对 IE6 及以下浏览器禁用 gzip
}
6.3.4 Brotli 压缩
是Google推出的新一代压缩算法,比Gzip压缩率更高,体积可以再减15%~30%,需编译第三方模块
# 下载 Brotli 模块源码
cd /usr/local/src
git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli
git submodule update --init
# 重新编译 Nginx
cd /usr/local/src/nginx-1.30.4
nginx -V
./configure [原有参数] --add-module=/usr/local/src/ngx_brotli
make -j$(nproc)
# 复制编译完成的二进制文件
cp objs/nginx /usr/sbin/nginx
# 配置 Brotli 压缩
http {
# Brotli压缩
brotli on;
brotli_comp_level 6; # 推荐5-8
brotli_min_length 512;
brotli_types text/plain text/css application/json application/javascript
text/xml application/xml image/svg+xml;
brotli_static on; # 优先使用预压缩的 .br 文件
# Gzip 作为降级方案(客户端不支持 Brotli 时使用)
gzip on;
gzip_comp_level 4;
gzip_types text/plain text/css application/json application/javascript;
gzip_vary on;
}
6.3.5 大文件直接 I/O
location /data {
directio 300M # 文件大小超过 300 M 开启直接 I/O
}
6.3.6 异步 I/O
aio on
6.3.7 线程池
高并发场景开启线程池,性能提升9倍左右
需要编译时添加编译选项 --with-threads
当需要读取本地文件时,创建 I/O 任务,投递到线程池任务队列
主线程立刻回归 epoll 事件循环,继续处理其它连接,不会等待文件读完
# 配置在最外层,配置格式
thread_pool <pool_name> threads=N [max_queue=M];
# 配置示例
thread_pool default threads=32 max_queue=65536;
6.4 LB 优化
6.4.1 代理缓冲优化
vim /etc/nginx/conf.d/proxy-lb.conf
location /api/ {
proxy_pass http://backend;
proxy_buffering on; # 开启缓冲,Nginx 接收完成再转发给客户端
proxy_buffer_size 4k; # 响应头的缓冲区大小
proxy_buffers 8 16k; # 响应体的缓冲区,8个16KB块
proxy_busy_buffers_size 32k; # 忙时缓冲区大小
}
6.4.2 超时优化
vim /etc/nginx/conf.d/proxy-lb.conf
location /api/ {
proxy_pass http://backend;
proxy_connect_timeout 5s; # 与后端建立连接的超时时间,建议 3-10s
proxy_send_timeout 30s; # 向后端发送请求的超时时间,建议 30-120s
proxy_read_timeout 30s; # 等待后端响应的超时时间,建议 30-120s
当出现 504 状态码时,建议调大 proxy_read_timeout 时间
6.5 5xx 状态码
| 状态码 | 含义 | 原因 | 排查方向 |
|---|---|---|---|
| 500 | 服务器内部错误 | 服务端代码执行出错 | 查看后端服务日志 |
| 502 | 网关错误 | 网关从后端服务收到无效响应.例如后端 Java 服务宕机,Nginx 无法连接 | 检查后端是否存活,端口是否监听 |
| 503 | 服务不可用 | 服务端过载或正在维护 | 检查后端负载,限流配置 |
| 504 | 网关超时 | 网关等待后端服务响应超时 | 检查后端响应时间,调大 proxy_read_timeout |
6.6 系统内核参数调优
# 允许 TIME_WAIT socket 复用(高并发必开)
net.ipv4.tcp_tw_reuse = 1
# TIME_WAIT 最大数量(太小会报错)
net.ipv4.tcp_max_tw_buckets = 65535
# TCP 全连接连接队列长度(net.core.somaxconn 必须大于 nginx 的 backlog)
net.core.somaxconn = 65535
# SYN 半连接队列长度(防 SYN Flood 攻击)
net.ipv4.tcp_max_syn_backlog = 65535
# SYN 重试次数(默认 5 次太慢)
net.ipv4.tcp_synack_retries = 2
# FIN_WAIT2 超时时间(默认60秒太长)
net.ipv4.tcp_fin_timeout = 15
# keepalive探测
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
# 本地临时端口范围(默认32768-61000)
net.ipv4.ip_local_port_range = 1024 65535
# TCP缓冲区
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# 网卡队列
net.core.netdev_max_backlog = 65535
# 文件描述符
fs.file-max = 20000000
fs.nr_open = 2097152
# 临时手段,半连接队列满时临时启用,高峰期过后关闭
net.ipv4.tcp_syncookie = 1
# 配置示例
vim /etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_tw_buckets = 65535
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.netdev_max_backlog = 65535
sysctl -p
临时手段 : syn-cookie
6.7 压测工具
# 安装 ab (Apache Bench)
dnf install -y httpd-tools
apt install -y apache2-utils
# 安装 装wrk
apt install -y wrk
6.7.1 ab 命令常用选项
| 选项 | 说明 | 示例 |
|---|---|---|
-n | 总发起请求数 | -n 100000 |
-c | 并发数 | -c 1000 |
-k | 启用长连接 | -k |
-H | 添加请求头 | -H "Accept-Encoding: gzip" |
-t | 压测持续时间,秒 | -t 30 |
-T | 指定 MIME 类型 | -T "application/json" |
-p | POST 请求的文件 | -p post_data.txt |
# 基础压测,10000个请求,1000并发
ab -n 10000 -c 1000 http://www.deng.org/
# 启用长连接
ab -n 10000 -c 1000 -k http://www.deng.org/
# 带 gzip 请求头压测
ab -n 10000 -c 1000 -H "Accept-Encoding: gzip" http://www.deng.org/
# 压测并记录结果到文件
ab -n 10000 -c 1000 http://www.deng.org/ > /tmp/bench_result.txt
6.7.2 ab 输出结果解析
P99 重点关注,长尾效应,大型网站由众多请求构成,多个请求叠加慢响应概率提升
Server Software: nginx/1.30.4 # 服务器软件及版本
Server Hostname: www.deng.org # 服务器主机名
Server Port: 80 # 服务器端口
Document Path: / # 请求路径
Document Length: 169 bytes # 响应体大小
Concurrency Level: 1000 # 并发数
Time taken for tests: 0.768 seconds # 总耗时
Complete requests: 10000 # 完成请求数
Failed requests: 128 # 失败请求数(必须为0)
(Connect: 0, Receive: 0, Length: 64, Exceptions: 64)
Non-2xx responses: 9936
Total transferred: 3576960 bytes # 总传输字节数(含头部)
HTML transferred: 1679184 bytes # HTML传输字节数
Requests per second: 13013.68 [#/sec] (mean) # QPS(每秒请求数)
Time per request: 76.842 [ms] (mean) # 平均响应时间(含所有并发)
Time per request: 0.077 [ms] (mean, across all concurrent requests)
Transfer rate: 4545.84 [Kbytes/sec] received # 传输速率
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 30 5.1 30 49 # TCP 连接时间
Processing: 15 44 9.7 44 75 # 服务端处理时间
Waiting: 0 32 8.5 34 63 # 等待响应时间
Total: 33 74 9.7 74 110 # 总时间
Percentage of the requests served within a certain time (ms)
50% 74
66% 76
75% 77
80% 79
90% 84
95% 93
98% 99
99% 99 # 99% 的请求在 99ms 内完成, P99 重点关注,长尾效应,大型网站由众多请求构成,多个请求叠加慢响应概率提升
100% 110 (longest request) # 最慢的请求在 110ms 内完成
6.7.3 wrk 常用选项
| 选项 | 说明 | 示例 |
|---|---|---|
-t | 线程数 | -t 4 |
-c | 并发连接数 | -c 100 |
-d | 压测持续数据 | -d 30s |
-H | 添加请求头 | -H "Accept-Encoding: gzip" |
--timeout | 超时时间 | --timeout 5s |
# 基础压测
wrk -t4 -c100 -d30s http://www.deng.org/
# 带 gzip 请求头
wrk -t4 -c100 -d30s -H "Accept-Encoding: gzip" http://www.deng.org/
# 输出详细结果
wrk -t4 -c100 -d30s --latency http://www.deng.org/
6.7.4 wrk 输出结果解析
Running 30s test @ http://www.deng.org/
4 threads and 100 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 2.01ms 270.41us 43.82ms 98.02% # 响应时间统计, Avg 越小越好 , Max 不能过大
Req/Sec 12.48k 191.61 14.80k 81.58% # 单线程QPS,数值越大越好
Latency Distribution
50% 2.00ms
75% 2.04ms
90% 2.09ms
99% 2.38ms # 99%的请求在2.38ms内完成,重点关注
1490453 requests in 30.01s, 518.81MB read # 总请求数/总数据量
Requests/sec: 49668.81 # QPS(每秒请求数)
Transfer/sec: 17.29MB # 传输速率

1477

被折叠的 条评论
为什么被折叠?



