首页 / 资讯中心 / 文章详情

Nginx入门图解:核心概念、常用命令与配置文件实战

Nginx入门图解:核心概念、常用命令与配置文件实战 ★ FEATURED ARTICLE
做后端开发这些年几乎每个项目都会和 Nginx 打交道。它不复杂但初学者往往一上来就被一堆名词和配置项搞得头晕什么叫反向代理location匹配规则为什么这么多nginx -s reload和 restart 到底有什么区别配置文件里那几十行到底哪些必须改、哪些不用管我最初学 Nginx 也是靠硬背后来把名词、命令、配置文件三个部分拼成一张“地图”才真正把 Nginx 用明白。这篇就用图解的思路把它拆开揉碎讲一遍。适合刚开始用 Nginx 的后端、运维和前端同学也适合那些配过几次但一直“知其然不知其所以然”的人。1. Nginx 核心名词图解先把几个概念放到正确的位置1.1 进程模型master 和 worker 各自在干嘛Nginx 启动后会同时存在两种进程一个 master 进程和多个 worker 进程。看下大概的结构master(主进程) ├── 读取配置、管理worker生命周期 ├── worker(子进程1) → 处理请求A、请求B、请求C... ├── worker(子进程2) → 处理请求D、请求E... └── worker(子进程N) → 处理请求...master 进程不直接处理业务请求它负责干这几件事启动时读取配置文件、创建/终止 worker 进程、接收管理员发的信号比如 reload、stop、记录错误日志。真正干活的是 worker 进程每个 worker 都是独立进程互相不共享内存事件循环各自处理进来的连接。这个模型最关键的优点是worker 崩了不影响其他 workermaster 会重新拉起一个新的。我之前遇到过某个业务模块导致 worker 内存暴涨被系统杀掉其他 worker 一点事没有请求只是短暂变慢不会整体挂掉。这就是多进程模型的价值也是 Nginx 在同类软件里稳定性口碑好的重要原因。还有个常见问题worker_processes到底配几个常规做法是等于 CPU 核心数。可以在服务器上跑nproc或者grep -c ^processor /proc/cpuinfo查看。配少了浪费 CPU配多了反而会因为进程上下文切换增加开销一般就是核心数不用盲目调大。1.2 正向代理和反向代理方向决定本质这个几乎是面试必问也是初学最容易混的点。我用一句话区分代理服务放在客户端那边帮客户端访问它访问不到的资源叫正向代理代理服务放在服务器那边帮服务器接收并转发请求叫反向代理。看下面的对比正向代理 客户端 → 正向代理服务器 → 目标服务器(比如某个外部网站) 客户端知道自己在用代理代理替客户端转发请求 反向代理 客户端 → 反向代理服务器(Nginx) → 后端服务器(比如Tomcat、Node服务) 客户端不知道自己连的是代理它以为Nginx就是最终服务器Nginx 最核心的用途就是反向代理。外部客户端请求到 NginxNginx 按配置把请求转发给内网里的业务服务器再把响应返回给客户端。这样做有实际好处后端服务器不用暴露公网 IP便于统一做拦截、限流、日志、HTTPS 证书管理还能顺手做负载均衡。前端同学常见的“跨域问题”多数时候也可以用 Nginx 反向代理解决原理就是让浏览器以为请求发给了同源的 Nginx再由 Nginx 转发给真正的后端接口。1.3 负载均衡的几种策略与选择逻辑反向代理最常见的配套能力就是负载均衡也就是把请求按一定策略分发给多台后端服务器。Nginx 原生支持的策略主要有以下几种策略默认/配置方式特点与适用场景轮询默认请求依次分发给每台后端适合后端配置相同的场景加权轮询weight2按权重分配比例适合机器性能有差异的场景ip_haship_hash;按客户端IP计算哈希同一IP固定落到同一台后端适合需要会话保持的场景least_connleast_conn;优先分给当前活跃连接数最少的后端适合后端负载不均明显的场景fair第三方fair;按后端响应时间分配响应快的多分需要额外模块支持我实际项目中用最多的是加权轮询和ip_hash。比如两台后端机器性能差别不大就默认轮询其中一台是新上线的机器先给个weight3让它多承担一些流量观察稳定性。涉及登录态的旧项目没有做共享 Session就用了ip_hash简单粗暴解决会话一致性问题。后来改造完会话集中存储又切回了轮询。需要注意ip_hash并不一定平均分配某些大出口IP下会有“倾斜”效果不要把它当成严格均衡方案。1.4 server、location、upstream配置语境里的三个“容器”这三个词常被混在一起提但它们承担的角色完全不一样。我习惯用“城市-小区-房间”来类比server是虚拟主机对应一个“小区”。它靠listen端口和server_name域名区分不同站点比如一个 Nginx 可以同时托管aaa.com和bbb.com各自写一个server块。location是server内部的路径匹配规则相当于“房间”。它专门负责把不同的 URL 路径分流到不同的处理方式比如/api/走代理、/static/走本地文件。upstream是后端服务器组对应“通信录里的联系人列表”。它定义一组后端地址供proxy_pass调用。举个例子upstream backend_servers { server 127.0.0.1:8081; server 127.0.0.1:8082; } server { listen 80; server_name api.example.com; location /api/ { proxy_pass http://backend_servers; } }这里upstream定义了两个后端地址server定义了对外服务的虚拟主机location负责把/api/开头的请求交给proxy_pass转发。三个容器各管一段理解了这一点配置文件的逻辑就清晰了一大半。2. 常用命令不要只记得nginx -s reload2.1 启动、停止与重启的信号逻辑Nginx 的命令行设计是基于信号机制的很多人只会nginx -s reload但理解信号后所有命令都好记了。先看最常用的几个nginx # 启动读取默认配置文件(/etc/nginx/nginx.conf) nginx -c /path/to/nginx.conf # 指定配置文件启动 nginx -s stop # 立即停止等同于强制退出 nginx -s quit # 优雅停止处理完当前请求再退出 nginx -s reload # 平滑重载配置 nginx -s reopen # 重新打开日志文件stop和quit的区别值得单独说。stop是直接终止进程正在处理的请求会断掉quit会等 worker 进程把当前连接处理完再退出。生产环境尽量用quit但stop在配置文件写法有问题、急需止血时反而更快。实际工作中reload是最常用的运维操作。它不中断服务原理是master 进程收到 reload 信号后重新读取配置文件并创建新的 worker 进程新旧 worker 短暂共存旧 worker 处理完手头请求后自动退出。所以你在线上新增一个location或修改反向代理地址后直接nginx -s reload就行不用停服用户体验完全不受影响。2.2 配置检查nginx -t是好习惯-T是排错利器修改完配置直接 reload 是有风险的。语法错误会导致 reload 失败更麻烦的是如果用自己的方式在错误配置文件下启动可能整个服务起不来。所以不管改多小的内容都建议先检查再重载nginx -t # 检查默认配置文件的语法 nginx -t -c /path/to/nginx.conf # 检查指定配置文件 nginx -T # 显示所有配置内容并做语法检查nginx -t输出syntax is ok和test is successful就说明语法没问题可以放心 reload。如果报错比如unknown directive xxxx说明你某个指令写错了或者缺少对应模块先检查拼写再看这个指令是不是被编译进当前 Nginx。nginx -T我强调一下它会输出 Nginx 完整加载后的配置包括所有 include 进来的子文件内容。配置文件多且分散时可以直接用一条命令看到最终生效的全体配置排查“我明明改了为什么没生效”这类问题特别有用。我之前排查过一个诡异现象改的是/etc/nginx/conf.d/下的某个文件但实际 Nginx 加载的是/usr/local/nginx/conf/里的另一个同名文件就是靠-T一眼看出加载来源不对。2.3 日志重开日志切割后必须要做的事nginx -s reopen是很多人忽略的命令但它对应一个非常重要的场景——日志切割。假设你按天归档日志到凌晨 00:00 把access.log重命名成access_20241120.log此时 Nginx 进程仍然持有原来那个文件的文件描述符会继续往已改名的文件里写日志新的access.log文件里反而什么都没有。这时候执行nginx -s reopenmaster 进程会重新打开access.log并写入。用 crontab 做日志切割的同学脚本里一定记得把nginx -s reopen加上否则日志永远切不干净。2.4 systemd 语境下的命令与直接信号的区别如果你用的是systemctl管理体系现在大多数发行版默认如此更推荐习惯这样systemctl start nginx systemctl stop nginx systemctl restart nginx systemctl reload nginx systemctl status nginxsystemctl reload nginx和nginx -s reload本质上效果一样都是平滑重载。区别在于 systemd 会跟踪服务状态restart是停掉再启动存在短暂中断systemctl reload则走平滑路径。另外直接用nginx -s reload的前提是你能直接执行这个命令而且 nginx 二进制在 PATH 里用 systemd 管理时配置文件和二进制路径通常更可控。还有一种是手动用 kill 发送信号kill -HUP $(cat /var/run/nginx.pid) # 平滑重载 kill -TERM $(cat /var/run/nginx.pid) # 优雅退出Nginx 启动后会把 master 进程的 PID 写入 pid 文件通常是/var/run/nginx.pid用kill配合信号本质上和nginx -s是一样的。理解这点后即使哪天nginx -s reload报找不到 pid 文件你也能自己按信号来处理。3. 配置文件逐块拆解一条主线读明白nginx.confNginx 配置文件表面看是一层层的花括号嵌套实际可以按这条主线去读全局配置 → events → http → server → location。每一层只关心自己的职责别混在一起理解。一个典型的配置骨架长这样user nginx; # 全局块worker进程运行身份 worker_processes auto; # 全局块worker进程数 events { # events块连接模型设置 worker_connections 1024; } http { # http块所有HTTP相关配置 include mime.types; sendfile on; server { # server块虚拟主机 listen 80; server_name example.com; location / { # location块路径匹配 root html; index index.html; } } }下面逐块看每个关键项的含义和为什么这么配。3.1 全局块运行身份和 worker 数量的起点全局块是配置文件最外层、events之前的那些指令。最需要关注的三个user指定 worker 进程以什么系统用户运行。默认可能是nobody或nginx。这里藏着一个常遇到的坑master 以 root 启动worker 以 user 指定的身份运行。如果你的root目录或者日志文件权限只有 root 能读而user nginx没权限读取访问就会出现 403。我之前就遇到过root /data/www;里的文件权限是700属主是 rootNginx worker 没有读权限页面一直 403排查了半小时才想起权限问题。worker_processes进程数。建议设为auto让 Nginx 自动按 CPU 核心数创建或者手动填核心数。这个值不是越大越好过大会消耗内存每个 worker 都要复制一份配置和模块且无谓增加调度开销。pidmaster 进程号写入路径默认/var/run/nginx.pid。改动它要谨慎因为 systemd、日志切割脚本、监控脚本往往都靠这个文件找进程。error_log全局错误日志路径和级别常见级别debug | info | notice | warn | error | crit。生产环境建议warn或error排查问题临时调到notice甚至debug可以获得更多信息但 debug 会产生海量日志别长期开启。3.2 events 块连接模型与并发上限events块主要影响 Nginx 如何处理网络连接。核心是两个指令worker_connections每个 worker 进程能同时保持的最大连接数默认 1024。Nginx 的最大并发连接数大致是最大并发 ≈ worker_processes × worker_connections但这个公式只对“Nginx 直接返回静态文件、不做反向代理”基本成立如果做反向代理一个请求往往要占用两个连接客户端到 NginxNginx 到后端实际并发能力要打个折扣。比如worker_processes 4、worker_connections 1024则理论上限约 4096反向代理场景下可能实际 2000 左右。真要支撑上万的并发要么调大worker_connections要么增加 worker 数量还要检查系统级文件描述符限制比如ulimit -n否则worker_connections配置再大也被系统限制卡住。use epoll在 Linux 上用 epoll 事件模型处理连接是高效并发的关键。现代 Nginx 大多会自动选最优模型一般不用手动写但了解它有助于理解“为什么 Nginx 的并发能力比 Apache 默认模式下强很多”——本质上就是事件驱动 非阻塞 I/O而不是每个连接一个进程。3.3 http 块公共功能开关区http块是整个配置最庞大的部分所有server的公共设置都在这里。常见项逐个记include mime.types;引入 MIME 类型映射表核心作用是让浏览器正确识别文件类型。少了它访问.css、.js文件时浏览器可能当成纯文本下载或直接显示乱码。sendfile on;开启内核态零拷贝把文件从磁盘发给客户端时减少用户态和内核态之间的数据拷贝大文件传输性能明显提升。静态资源站点基本都会开。keepalive_timeout 65;客户端连接的空闲超时时间。设太短频繁建立新连接增加开销设太长空闲连接占用资源。默认 60 秒左右够用。gzip on;和gzip_types开启响应压缩对文本类资源效果显著CSS/JS 经常能压到原来的三分之一。但要注意图片、视频类文件本身已压缩开了反而浪费 CPU所以gzip_types只列文本类就好。log_format定义访问日志格式。我常用的精简格式是这样的log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent;然后在server里access_log /var/log/nginx/access.log main;引用。日志字段不是越多越好字段多了日志文件膨胀快、清洗也麻烦按团队实际需求取舍。3.4 server 块与 location 块虚拟主机与匹配规则server块里核心是listen和server_name的配合。listen 80;监听的端口server_name绑定域名。一个 Nginx 可以写多个server靠这两个字段区分不同的网站入口。当多个server的listen相同Nginx 会按server_name的匹配优先级选虚拟主机。location的匹配规则是另一个容易出问题的地方。我尽量用“匹配优先级”来总结location /xxx精确匹配优先级最高匹配到后不再继续。location ^~ /static/前缀匹配且命中后不再检查正则。location ~ /xxx和location ~* /xxx正则匹配按配置顺序匹配区分大小写/不区分大小写。普通字符串前缀匹配按最长匹配优先。location /healthz { return 200 ok; # 精确匹配健康检查路径 } location ^~ /static/ { alias /data/static/; # 前缀匹配静态资源命中后不再走正则 } location ~* \.(png|jpg|css|js)$ { expires 30d; # 正则匹配常见静态文件设置长缓存 } location /api/ { proxy_pass http://backend_servers; proxy_set_header Host $host; }其中alias和root的区别非常容易踩坑。root会把完整的 URL 路径拼在指定目录后面比如location /static/ { root /data/www; }请求/static/a.css实际查找的是/data/www/static/a.css注意路径里带了/static/。而alias是替换匹配的部分location /static/ { alias /data/files/; }请求/static/a.css实际查找的是/data/files/a.css/static/被替换掉了。很多人把两者混用导致文件 404 或者路径不对学会先看“URL 路径是否保留”来判断该用哪个。4. 配置中最容易翻车的几个点与排查思路配置文件不报错不等于配置正确业务的问题往往发生在运行期。以下是我实际踩过或者帮别人排查过的高频问题。4.1proxy_pass末尾带不带斜杠效果完全不同这是反向代理里最容易犯的错。看两个写法# 写法A不带URI location /api/ { proxy_pass http://backend_servers; }请求/api/user/info转发后后端收到的路径还是/api/user/info。# 写法B带 URI末尾斜杠 location /api/ { proxy_pass http://backend_servers/; }请求/api/user/info转发后Nginx 会把location匹配到的/api/部分替换成/后端收到的是/user/info。简单说proxy_pass后面带路径含/就会替换匹配部分不带路径就保留原始 URI。但如果是location /xxx这类精确匹配或者location用正则匹配则proxy_pass通常不能带 URI。实际项目里出现“接口 404”“路径多了一层”基本都是这里没理清楚。排查方法后端接口用假路由打印一下收到的 URL或者直接看 Nginx access log 里的$request字段能明显看到转发前后路径差异。4.2reload没生效检查 include 和缓存改完配置文件执行nginx -t通过、nginx -s reload成功但访问效果没变。这种情况最常见的两个原因第一个是主力配置文件的include被覆盖。比如你改的是/etc/nginx/conf.d/xxx.conf但主配置里 include 的是另一个目录或者有两份配置文件同时定义了同一个server_nameNginx 按加载顺序选了第一份。用nginx -T一次性看全量配置快速定位实际加载的是哪一份。第二个是浏览器缓存。静态资源返回了expires 30d;之类的长缓存头后端文件已经换了浏览器还直接读本地缓存看起来像“Nginx 没生效”其实是缓存策略太激进。排查时先强制刷新CtrlShiftR再用curl -I看响应头里的Cache-Control、Last-Modified字段确认。4.3 端口冲突启动或者 reload 时收到bind() to 0.0.0.0:80 failed (98: Address already in use)说明端口被占用。先用ss -lntp | grep 80或lsof -i:80找到占用进程要么停掉它要么换个端口。在容器环境里尤其要确认多个 Nginx 容器是否映射了同一个宿主机端口。4.4 502 与 504 的排查方向502 Bad Gateway通常是 Nginx 连不到后端504 Gateway Timeout是连到了但后端超时没返回。排查 502 的顺序先检查后端进程是否存活再看 Nginx 到后端的网络连通性curl一下后端地址再确认proxy_pass里写的地址和端口与后端实际监听是否一致。我碰到过一次很隐蔽的 502后端进程还在但监听的 IPv6 地址Nginx 配置里写的是 IPv4 地址连不上。后来在upstream里改成server [::1]:8080;才解决。排查 504 则要看后端处理耗时。如果是大文件上传或者某些慢查询接口需要调整proxy_read_timeout、proxy_send_timeout。默认 60 秒往往不够要结合业务场景调大但不能无限调大因为后端真卡死时也得靠超时触发告警。4.5 403 不只是权限问题也可能是目录索引被关遇到 403先检查user配置的用户对root或alias目录有没有x权限。Linux 对目录的 x 权限代表“能否进入目录”没有 x 权限连读取文件列表都不行。可以用ls -ld /data/www先看一眼。另外如果你的location /配置了autoindex on;但当前目录没有索引文件index.html也没开启目录列表也会出现 403。此时要么补一个索引文件要么开启autoindex on;允许浏览目录。4.6 日志是排错的第一入口其次是curl -I排查任何 Nginx 问题我建议的顺序永远是看错误日志 → 看访问日志 → 抓请求响应。错误日志默认位置是/var/log/nginx/error.log这里有启动失败、权限问题、worker 异常、上游连接失败等关键信息。访问日志能确认“请求到底有没有到 Nginx、返回了哪个状态码”。curl -I http://你的域名/xxx能帮你快速复现问题而且能直接看到响应头。比如返回的Content-Type不对、Location跳转目标不对、缓存头异常一下就能定位到是哪一层配置出的问题。实在搞不定临时把错误日志级别调到debug看 Nginx 每步处理的详细过程但用完记得调回来不然磁盘撑不住。5. 我的一些配置习惯可能对你有用关于配置文件管理我的习惯是主配置只保留全局设置和include所有业务相关的server全部拆到/etc/nginx/conf.d/下的独立文件里命名按业务来比如shop.conf、pay.conf。这样改一个站点不会影响其他站点reload 出问题也容易回滚直接用 Git 管理整个/etc/nginx/目录每次改动都留痕。这个习惯看起来不起眼但在多业务共用一个 Nginx 的时候能省很多事。如果刚开始接触 Nginx建议先在自己电脑上装一个用nginx -T和nginx -t反复改配置看结果亲手复现一遍“改错配 403”“reload 不生效”这类问题比看十篇教程都有用。等能熟练看懂error.log里的每一行报错Nginx 基本就算真正掌握了。
阅读完成 · 觉得有帮助?
咨询建站