ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Nginx核心功能实战指南:从反向代理到高并发调优

Nginx核心功能实战指南:从反向代理到高并发调优 做运维这些年Nginx 装了没有一百遍也有几十遍从最初的编译安装到后面的平滑升级、反向代理、负载均衡、静态文件服务几乎每个环节都踩过坑。后来发现很多刚接触 Nginx 的朋友一上来就搜“nginx教程”、“nginx配置”、“nginx反向代理”搜到的内容零零散散看完了也不知道到底怎么组合使用。今天我就把 Nginx 的核心功能从头到尾捋一遍把安装、配置、调优、排障这些真正用得上的东西讲清楚适合刚入行的运维、后端开发以及准备 Nginx 面试题的朋友。1. Nginx 到底是什么从“反向代理”这张名片说起1.1 高性能的根源事件驱动与进程模型很多人第一次接触 Nginx 是因为反向代理但 Nginx 真正厉害的地方是它的进程模型。Nginx 启动后有一个 master 进程和多个 worker 进程master 负责加载配置、管理 worker 生命周期worker 负责真正处理请求。每个 worker 内部是事件驱动的用的是 epollLinux 平台这种异步非阻塞机制好比一个服务员同时接待几十桌客人哪桌有需要就过去处理而不是每来一桌客人就专门派一个服务员盯到结束。这就是 Nginx 高并发的底气。对比一下传统的进程/线程模型比如 Apache 的 prefork 模式每来一个连接就要 fork 一个进程连接多了内存和 CPU 开销会直线上升。Nginx 的方式是“少量进程 事件循环”连接数再多worker 数量基本不变内存占用非常稳定。这也是为什么很多面试题会问“Nginx 为什么快”答到 master-worker 模型、事件驱动、epoll、非阻塞 I/O 这几个关键词再结合请求处理流程展开基本就能拿分。我实测过一个只有 2 核 2G 的小云主机Nginx 单机扛住 3 万左右的并发连接没什么问题如果只是静态文件访问甚至可以更高。当然这里的上限还受带宽、文件描述符、内核参数影响后面调优部分我会细说。1.2 一个请求的完整旅程理解了进程模型再来看一个 HTTP 请求是怎么走的。用户发起请求后先是内核接受 TCP 连接Nginx 的 worker 通过 epoll 感知到事件读请求头、解析 Host、URI、查询参数然后按照配置文件里的 location 规则去匹配。匹配到 location 后根据里面的指令决定怎么处理要么直接返回静态文件要么把请求转发给后端反向代理要么返回 302 跳转要么返回自定义错误页。这里要注意的是Nginx 本身只能处理静态内容和做转发它不认识 JSP、PHP 这类动态脚本。很多新手问“nginx 支持 jsp 吗”答案是不支持正确做法是把动态请求通过反向代理转给 Tomcat 或 PHP-FPM。举个典型场景location 里以 .jsp 结尾的请求全都 proxy_pass 到 8080 端口的 Tomcat静态资源则直接走 Nginx 本地磁盘。这种动静分离方案在面试里也是个高频考点。2. 安装部署盘点从 yum 到纯内网离线编译2.1 Linux 三种安装方式怎么选Linux 上装 Nginx 大体三选一发行版自带的包管理器、官方源码编译、官方预编译 rpm 包。用包管理器最简单比如 Ubuntu 的 apt、CentOS/AlmaLinux 的 dnf直接yum install nginx或者dnf install nginx装完就能跑。好处是有 systemd 管理、目录符合发行版规范升级也方便缺点是版本通常偏旧想用最新特性或者自定义编译模块就比较费劲。源码编译安装是运维老哥最熟悉的方式。需要先准备依赖库pcre2或老的 pcre我建议用 pcre2、zlib、openssl以及 gcc、make 这些基础工具。这里有个老坑网上不少教程让你从 pcre 官网下载源码推荐 pcre-8.45理由是它和 Nginx 兼容性好。但如果你用的系统是 AlmaLinux 9、Ubuntu 22.04 这种较新的发行版我更推荐直接用 pcre2Nginx 1.25 以上对 pcre2 支持已经很成熟没必要抱着 8.45 不放。AlmaLinux 9 上我通常的做法是dnf install -y gcc make pcre2-devel zlib-devel openssl-devel wget https://nginx.org/download/nginx-1.26.2.tar.gz tar -zxvf nginx-1.26.2.tar.gz cd nginx-1.26.2 ./configure --prefix/usr/local/nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module make make install编译参数里的--with-http_ssl_module一定要加否则后面配 HTTPS 你会发现 server 里写listen 443 ssl;直接报错说不认识 ssl 参数。--with-http_v2_module是 HTTP/2 支持性能敏感场景建议开启。编译完记得ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx否则每次都得敲全路径。2.2 aarch64 和纯内网环境怎么离线装国产化服务器越来越常见aarch64 架构的机器装 Nginx 跟 x86_64 差别不大源码编译基本一路畅通。真正的痛点是纯内网环境不能访问外网yum 源也没有这时候你要么提前准备离线 rpm 包要么准备源码包。离线 rpm 的做法是找一台同架构、同发行版的能上网机器用yumdownloader --resolve把 nginx 和它依赖的 pcre2、zlib、openssl 全部下载下来拷贝到内网后rpm -ivh *.rpm但要注意依赖顺序。如果依赖太乱我会直接选择源码编译——依赖库也就 openssl、pcre、zlib 这几个连同 nginx 源码一起拷进去./configure时用--with-pcre/path/to/pcre指定本地源码路径就可以离线完成整个编译。我踩过的坑是纯内网服务器经常忘记装 gcc。内网没有 yum 源时gcc 也要提前拷过去。建议在内网机器上先执行gcc --version确认没有的话一起带上 gcc、glibc-devel、make 这组基础编译工具链。2.3 Windows 下安装与常见坑Windows 装 Nginx 其实是最简单的官网下载 Windows 版本 zip 包解压完就能用。注意 Windows 版是一堆 exe没有编译过程也不支持 chroot、user 等 Unix 专属指令。启动方式是进入解压目录执行start nginx停止是nginx.exe -s stop重载配置是nginx.exe -s reload我这里想特别说明一个 Windows 下的经典报错nginx: [emerg] createfile() d:/phpstudy_pro/www/admin2.com/.htaccess failed。这个一看就是在 Windows 下用 phpStudy 或者手动指定了错误路径。常见原因有三个路径里的目录根本不存在目录存在但 Nginx 进程没有写权限配置文件里 root 指到了文件而不是目录。解决方法很简单把 root 指向真实存在的目录例如root D:/phpstudy_pro/www/admin2.com;并确认该目录有读权限。Windows 版主要用来本地调试和测试配置不建议上生产环境——性能和稳定性跟 Linux 差不少。2.4 卸载与残留清理卸载 Nginx 这事看着简单坑一点不少。yum 安装的可以直接yum remove nginx但 /etc/nginx/ 下的配置、/var/log/nginx/ 下的日志不一定会删干净需要手动清理。源码编译安装的更麻烦make uninstall有时候并不存在也未必删得全最稳的方法是rm -rf掉安装前缀目录比如rm -rf /usr/local/nginx再删掉自建的 systemd service 文件、软链接和编译时产生的临时目录。我习惯在卸载前先备份 /etc/nginx 和日志避免误删后恢复不了。3. 高频配置实操反向代理、负载均衡、SSL、多站点3.1 反向代理与 proxy_pass 的斜杠玄机反向代理是 Nginx 最核心的功能之一。一个最基础的配置长这样server { listen 80; server_name api.example.com; location / { proxy_pass http://192.168.1.10: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_pass 后面带不带 “/”转发后的路径完全不一样。如果 location 是/api/proxy_pass 是http://192.168.1.10:8080那么请求/api/user会被原样转发成/api/user如果 proxy_pass 写成http://192.168.1.10:8080/请求/api/user会被去掉匹配到的/api/前缀转发成/user。换句话说带不带斜杠决定了是否保留 location 前缀具体要看后端接口设计。我每次改完都会用nginx -t验证再 curl 一下后端路径确认没转发错。另外一个反向代理的长连接配置也值得重视。Nginx 默认向后端走的是 HTTP/1.0 短连接每次都要重新建 TCP高并发下握手开销非常大。开启 upstream keepalive 的姿势是upstream backend { server 192.168.1.10:8080; server 192.168.1.11:8080; keepalive 32; } server { listen 80; location / { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Connection ; } }keepalive 32表示每个 worker 进程最多保持 32 个空闲的 upstream 连接不是总连接数上限。配合proxy_http_version 1.1;和空的 Connection 头长连接才能生效。我在压测里观察过开启 keepalive 后 QPS 能提升个 20% 到 50%尤其后端是 Tomcat 这类对建连敏感的服务改善明显。3.2 upstream 负载均衡与健康检查upstream 是 Nginx 做负载均衡的关键模块。最常用的分配策略有轮询、权重、ip_hash、least_conn。权重适合后端机器配置不均的场景upstream backend { server 192.168.1.10:8080 weight3; server 192.168.1.11:8080 weight1; server 192.168.1.12:8080 backup; }weight3 表示这台机器分到 3 份请求weight1 分到 1 份backup 是备用服务器非特殊情况下不参与。ip_hash 是按客户端 IP 的哈希值分配这样同一个用户会固定打到同一台后端适合有 Session 的旧系统。least_conn 则会把请求给当前连接数最少的那台适合长连接和耗时不均的场景。这里要提醒一点upstream 里的被动健康检查默认是“转发失败后标记不可用”由 max_fails 和 fail_timeout 控制比如server 192.168.1.10:8080 max_fails2 fail_timeout30s;表示 30 秒内失败 2 次就摘掉这台30 秒后再试探。但被动检查在极端情况下会有延迟感知的问题如果后端重启比较频繁建议配合脚本或者商业版主动健康检查。3.3 SSL 证书配置以 Nginx 类型证书为例HTTPS 已经是标配了。在 Godaddy 这类服务商购买证书时会让你选服务器类型选择“Nginx”之后下载的压缩包通常是 .crt 或 .pem 证书文件加 .key 私钥文件。Nginx 配置的关键就两行server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; location / { root /var/www/html; index index.html; } }如果还需要把 HTTP 请求跳转到 HTTPS最简单的是再监听一个 80 端口然后 return 301server { listen 80; server_name example.com; return 301 https://$host$request_uri; }我踩过的坑有些证书是证书链不完整比如服务器只配了域名证书没有配 CA 中间证书Android 手机或者某些浏览器就会提示“证书不可信”。解决办法是在证书文件里把域名证书和中间证书按顺序拼接cat example.com.crt intermediate.crt example.com.chained.crt然后在 ssl_certificate 里指向拼接后的文件。另一个容易出错的场景是客户端报no required ssl certificate was sent。这通常不是 Nginx 配置错误而是服务端配了客户端证书验证双向 TLSssl_verify_client on;但客户端发起请求时没带客户端证书。用 curl 访问这类接口时要显式指定证书和私钥curl --cert client.crt --key client.key https://example.com如果是 Java 代码调用则需要把客户端证书打包成 PKCS12 导入密钥库。3.4 多域名、动静分离与 autoindex 在线文件浏览Nginx 配置多个网站非常直观一个 server 块就是一个站点靠 server_name 区分。比如主域名www.example.com和二级域名blog.example.com各自写一个 server 块listen 同端口也没问题Nginx 会根据请求的 Host 头做匹配。如果访问的域名没匹配到任何 server_name默认会走第一个 server 块所以我习惯把第一个 server 块配成默认拒绝server { listen 80 default_server; server_name _; return 444; }return 444 是直接关闭连接不给任何响应比返回 403 更节省资源。动静分离就是把静态资源图片、CSS、JS直接丢给 Nginx 的本地目录动态接口转发给后端。比如location ~* \.(js|css|png|jpg|gif|svg)$ { root /var/www/static; expires 7d; }配合 expires 设置缓存时间能明显减轻后端压力。autoindex 在线文件浏览是很多人做内部共享文件时喜欢用的功能。开启方法很简单location /files/ { alias /data/share/; autoindex on; autoindex_exact_size off; autoindex_localtime on; charset utf-8; }autoindex_exact_size off 表示显示大小用人类可读的 KB、MB 格式on 则是精确到字节autoindex_localtime on 让文件时间显示本地时间而不是 UTC。如果不想让所有人都能看到可以加一层用户名密码认证location /files/ { auth_basic Restricted; auth_basic_user_file /etc/nginx/htpasswd; }htpasswd 文件可以用系统自带的 htpasswd 命令生成也可以用 openssl passwd 手动算。我在生产环境用 Nginx 做过一个简单的内部资料库几千个文件在 autoindex 模式下浏览和下载都非常流畅只是需要注意大文件断点续传默认是支持的但下载限速得额外配 limit_rate。4. 高并发调优把 Nginx 压榨到极限4.1 核心参数与计算逻辑高并发是 Nginx 的招牌但“装好就能高并发”是个误解。默认配置只适合小流量场景真正要扛住高并发几个核心参数必须调整。第一是 worker_processes通常设为和 CPU 核数一致可以使用worker_processes auto;让 Nginx 自己检测。第二是 worker_connections这个值决定每个 worker 进程能同时打开的最大连接数一般建议从 10240 起步提高到 65535 也可以但要小心系统文件描述符限制。理论最大并发连接数可以简单估算max_clients worker_processes * worker_connections。例如 8 核机器每个 worker 配 10240理论最大 81920 个连接。如果是反向代理场景客户端到 Nginx 和后端到 Nginx 各占一个连接实际可支撑的客户端并发大约是理论值的一半如果是静态文件服务才是接近理论值。所以不要照抄网上的“十万并发”配置先想清楚自己的场景是纯静态还是带 upstream再决定参数。同时还要调整系统内核参数和进程限制不然 Nginx 会报worker_connections are more than open file limit。需要改 /etc/security/limits.conf 或 systemd 服务的 LimitNOFILE再改内核参数sysctl -w fs.file-max6553500 sysctl -w net.ipv4.ip_local_port_range1024 65535 sysctl -w net.ipv4.tcp_tw_reuse1tcp_tw_reuse 是为了尽快复用 TIME_WAIT 状态的端口对短连接转发场景很有帮助。改完用sysctl -p生效Nginx 也要重启。4.2 静态资源缓存与 gzip除了连接层面的调优传输层面的优化也很关键。gzip 是必开的尤其是文本类资源压缩率通常在 60% 到 80%gzip on; gzip_comp_level 6; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svgxml;gzip_comp_level 不是越高越好我一般用 6再高 CPU 开销明显上升但压缩率提升有限。如果前端用了 Webpack 这类工具已经把文件压缩成 .js.gz 了后端再压一遍就是浪费这种情况下可以用gzip_static on;优先发送预压缩文件。静态文件还有几个高效的配置项我经常组合使用sendfile on; tcp_nopush on; open_file_cache max100000 inactive20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on;sendfile 让数据直接从磁盘到网卡减少内核态和用户态之间拷贝open_file_cache 缓存文件句柄避免每次请求都重复 open 文件。实测在纯静态场景下这几项组合跟默认配置相比QPS 能提高一倍不止延迟也会明显下降。4.3 安全加固与漏洞修复以 CVE-2022-41742 为例Nginx 的漏洞公告不能忽视。比如热词里提到的 F5 Nginx 缓冲区错误漏洞 CVE-2022-41742这是一个在特定配置下可能触发的安全缺陷攻击者可能通过构造特定请求导致进程崩溃或潜在风险。面对这类漏洞最直接的处理思路是关注影响版本范围把 Nginx 升级到已修复的版本不要心存侥幸。线上安全无小事我习惯每周五下午花十分钟去官网看一眼版本更新和 security 公告。除了升级日常还要做几件小事一是把server_tokens off;关掉避免响应头里带 Nginx 精确版本号二是限制请求体大小client_max_body_size 10m;防止超大 Body 拖垮 worker三是针对敏感接口加限流limit_req_zone按 IP 或按 URI 做限制比如登录接口每 IP 每秒最多 5 次。这些配置能挡掉不少基础攻击但别指望 Nginx 能防业务层 SQL 注入、越权这类问题那得靠应用层解决。5. 平滑升级与常见排障实录5.1 平滑升级的原理与操作Nginx 版本升级最怕的就是中断服务。平滑升级的核心原理是老 master 进程不退出新二进制先启动一个新的 master两个进程同时存在新请求由新进程处理然后让老 worker 优雅退出最后老 master 退出。整个过程对用户来说几乎是无感知的。我每次升级前都会用nginx -V把旧版本的编译参数完整记下来最好存成笔记。然后按以下步骤走# 查看旧版本编译参数 nginx -V # 下载新版源码并用完全相同的 ./configure 参数编译 ./configure 旧版本一模一样的参数 make # 备份旧二进制 cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old # 复制新二进制 cp objs/nginx /usr/local/nginx/sbin/nginx # 平滑升级 make upgrade手动执行也可以kill -USR2 $(cat /usr/local/nginx/logs/nginx.pid)启动新 master然后kill -QUIT $(cat /usr/local/nginx/logs/nginx.pid.oldbin)关闭旧 master。这里要注意旧的 pid 文件会变成 nginx.pid.oldbin升级后最好确认一下新进程是否正常监听端口。如果新版本有问题想回退就把 nginx.old 复制回去再执行kill -HUP $(cat ...pid)让 Nginx 重新加载旧配置。平滑升级期间建议通知监控团队盯紧错误率我见过一次升级后 HTTP/2 握手异常就是靠监控曲线第一个发现的。5.2 常见错误与排查思路速查表Nginx 的报错信息其实很友好关键在于知道每类报错背后查什么。我把遇到频次比较高的整理成了表格方便大家对着排查。报错信息常见原因排查与解决[emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)端口被占用ss -lntp找到占用进程一般是另一个 Nginx 或 Apache停掉或改端口[emerg] createfile() d:/... failedWindows 下 root 路径不存在或无权限检查目录是否存在、权限是否足够路径是否有拼写错误[emerg] mkdir() /var/cache/nginx/client_temp failed (13: Permission denied)缓存目录无写权限创建目录并 chown 给运行 Nginx 的用户[alert] 0 worker_connections are not enoughworker_connections 配小了调大 worker_connections并同步提高系统 fd 上限[error] upstream sent too big header while reading response header from upstream后端返回的响应头太大默认缓冲不够增加proxy_buffer_size 8k;和proxy_buffers 8 8k;no required ssl certificate was sent双向 TLS 场景中客户端没带客户端证书curl 加--cert、--keyJava 代码配置 PKCS12 密钥库502 Bad Gateway后端服务挂了或超时确认后端端口存活调大proxy_read_timeout观察后端日志403 Forbidden目录权限或 autoindex 配置不对检查 root 目录可读autoindex on 时确认目录存在文件5.3 配置检查与格式化每次改完配置我至少做两件事一是nginx -t检查语法二是systemctl reload nginx平滑重载配置。注意 reload 不等于 restartreload 是让 master 重新加载配置、优雅重启 worker不用停服。如果配置文件有语法错误reload 会失败并提示具体文件行号这是用 Nginx 最该养成的好习惯。很多人在本地用 VSCode 编辑 nginx 配置写得再规范也免不了缩进混乱。VSCode 里装个 nginx 格式化插件或者用 nginxfmt 这类命令行工具可以对 server、location 块自动整理缩进。但格式化之后一定要记得nginx -t我就碰到过格式化工具把proxy_set_header后面的引号搞丢的情况。另外一些在线格式化网站会把敏感配置贴上去我不建议这么做内部域名、IP、证书路径都是敏感信息本地工具处理才是安全的。6. 关于面试与学习路线的几点心得6.1 面试常考的几个核心问题因为这类关键词在热词里出现我也多说两句。Nginx 面试题翻来覆去基本都是这些方向Nginx 为什么高并发、正向代理和反向代理的区别、负载均衡有哪些策略、如何实现 session 保持、Nginx 和 Apache 怎么选、如何做高可用。前两个前面已经讲透了负载均衡策略在 3.2 节里也说了这里补充一个高可用的思路Nginx 本身是单点普通做法是在两台机器上分别部署 Nginx用 Keepalived 或 VRRP 绑定一个虚拟 IP主节点挂了 VIP 自动漂移让备用节点接管。面试时最容易翻车的是被问到“Nginx 是如何实现反向代理的”很多人只会背配置说不出 proxy_pass 的转发逻辑、Connection 头为什么必须设置成空、HTTP/1.1 与 HTTP/1.0 的区别。建议先把本文 3.1 节的原理吃透再去看官方文档基本不会再慌。6.2 推荐学习路径与工具想系统学 Nginx最好的老师是官方文档和本机实操。先把安装、静态服务器、反向代理、负载均衡、SSL 这五件事做一遍再进阶看 rewrite、map、变量作用域、nginx 内置变量以及 lua 和 OpenResty。学的过程中可以用 Nginx Proxy Manager 这类基于容器和图形界面的工具辅助理解它把域名、SSL 证书、反向代理的配置都封装成了表单对新手非常友好也适合内部快速搭建网关。但不要停留在只会点界面命令行里的配置逻辑才是通用技能。调试方面建议在本地准备好一套 Linux 虚拟机或者容器把nginx -t、curl -v、ss -lntp这三个命令练到条件反射。我看很多新手遇到连不上 Nginx第一反应是翻配置其实应该先用 curl 本地访问确认 Nginx 是否在监听再逐层排查防火墙、端口和后端服务。最后想说的如果把 Nginx 比作一把瑞士军刀反向代理、负载均衡、静态服务、SSL 终结、限流防刷这些功能就是不同的刀片单独用很简单但在一个真实项目里把它们组合得当才是真正的核心能力。我个人在实际操作中的体会是Nginx 的高并发不是靠某一两个参数调出来的而是从系统内核、事件模型、后端连接、传输压缩到业务隔离层层叠加的结果。先在自己负责的项目里从最小的反向代理开始把一个配置彻底玩明白再逐步加上负载均衡、缓存、HTTPS你会发现自己对“ Nginx 核心功能”的理解已经不再是背几个配置片段而是真正知道每一步为什么这么写、出了错该往哪个方向查。
返回列表