十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Nginx实战:反向代理、负载均衡与部署避坑指南

Nginx实战:反向代理、负载均衡与部署避坑指南 刚接手一个项目的时候经常会被问到一个问题服务器上那么多进程到底谁在负责对外流量第一次接触生产环境的人往往会把Tomcat的地址直接暴露出去结果并发一上来就各种超时日志里全是连接被拒。后来我把Nginx架在最前面才明白过去缺的不是更强的服务器而是一个能把请求统一收口、再分发给后端的入口层。这篇就围绕Nginx的作用和应用场景结合我这些年实际部署踩过的坑把这块讲透。Nginx本质上是一个高性能的HTTP和反向代理服务器同时也能承担负载均衡、静态资源托管、SSL终止、缓存加速这些任务。它最厉害的地方在于用极少的系统资源就能扛住大规模并发连接所以无论是个人博客、中小型企业站点还是日活百万的应用几乎都会在架构里见到它的影子。无论你是刚入门Linux部署的新手还是准备优化现有服务的老手只要搞懂Nginx的职责边界和配置思路很多所谓的疑难杂症都能自己排查。1. 先把Nginx的本质讲清楚它和普通Web服务的底层差异很多教程上来就让你敲apt install nginx装完改配置也不说为什么它能扛高并发。我在不熟悉它内部机制的那段时间遇到性能问题就只能加服务器代价极高。后来啃了一遍Nginx的进程模型和事件处理机制才算真正理解它。1.1 基于事件的并发模型才是它高性能的根传统的老牌Web服务器比如早期的Apache默认每个连接会对应一个进程或线程请求多了线程就多线程一多CPU都在做上下文切换真正处理业务的时间就被挤占了。Nginx走的是另一条路它用事件驱动模型一个进程可以同时盯着成千上万个连接哪个连接有数据来了就处理谁没有数据就继续干别的。小到静态文件大到反向代理转发这个模型都能用很少的线程支撑很高的并发。打个比方传统模式像餐厅里一个服务员只管一桌客人客人多就得招更多服务员Nginx模式像一个训练有素的服务员同时照看几十桌谁举手就过去服务谁餐厅租金不变但接待能力上去了。事件驱动带来的另一个好处是CPU占用非常平滑。即便请求量瞬时上涨只要worker进程数量设置合理系统负载也不会像进程模型那样暴涨。1.2 Master-Worker进程结构多进程配合而非多线程Nginx启动之后会有一个master主进程和若干个worker工作进程。master负责读取配置、管理worker生命周期、平滑重载worker才是真正处理请求的角色。两者通过共享内存和信号机制协作。这个结构带来两个实用价值第一某个worker崩溃不会拖垮整个服务master会立刻拉起新的worker第二nginx -s reload可以做到不停机重载配置因为master会先加载新配置再启动新worker最后优雅关闭旧worker正在处理的请求会走完而不是被直接掐断。实际运维里我见过有人把worker_processes配置成跟CPU核心数一样就完事这通常没问题。不过要注意如果机器上同时跑着其他吃CPU的服务适当调低worker数量反而更稳避免Nginx把核心占满。1.3 为什么说它“轻量又灵活”轻量体现在内存占用和安装包体积上。一个Nginx进程在空闲时的内存占用往往只有几十MB相比动辄几百MB的中间件来说非常节约资源。灵活则体现在配置粒度上从全局到server、再到location每一层都能单独控制行为还能通过include把配置拆分成多个文件管理。这也是大型项目里必须养成的习惯不要把全部配置塞进一个nginx.conf按站点或域名拆文件后续维护会轻松很多。2. 高频场景一反向代理作为统一入口2.1 反向代理到底代理了什么先区分两个概念正向代理是替客户端去访问服务器比如你在内网通过代理访问外网资源反向代理则是替服务器接收客户端的请求客户端只和代理打交道并不知道真正的后端服务在哪台机器上。Nginx最常见的角色就是这个“前台接待员”。浏览器请求https://api.example.comNginx收到后根据location规则把请求转发给内网的Tomcat、TongWeb、Spring Boot或者任意后端服务。客户端始终只看到Nginx的地址后端服务器可以躲在防火墙后面安全性明显提升同时还能在代理层统一做超时、缓存、限流、日志记录。2.2 最简反向代理配置和常见误区一份最基础的反向代理配置大概长这样server { listen 80; server_name api.example.com; location / { proxy_pass http://127.0.0.1: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后面的URL是否带URI行为差别很大。不带斜杠、不带路径比如http://127.0.0.1:8080表示将原始请求URI完整传给后端如果写了http://127.0.0.1:8080/则会把location匹配部分的路径替换掉。这块非常容易出错我在早期就遇到过前端页面能开接口却404的场景最后排查发现就是proxy_pass末尾多了一个斜杠。2.3 反向代理中容易忽略的Header传递细节如果后端有获取真实IP的需求X-Real-IP和X-Forwarded-For必须显式传递。还有WebSocket场景需要额外加两个Headerproxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade;不加这两行WebSocket连接会在代理层被截断。另一个容易忽视的是大文件上传默认的client_max_body_size只有1MB后端接口能收大文件但请求经Nginx转发时直接返回413。这个我在对接文件上传服务时踩过配置里加一句client_max_body_size 50m;就能解决。3. 高频场景二负载均衡策略与实际选型当后端服务不止一台Nginx的upstream模块就可以派上用场。它把多台后端机器定义成一个服务器组Nginx按照配置的策略将请求分发到组内成员这样即使单台机器扛不住压力也能靠横向扩展解决问题。3.1 常见分发规则Nginx默认的策略是轮询每个请求按顺序分给不同后端适合后端机器配置相同的场景。加权轮询则通过weight参数让性能更好的机器承担更多流量upstream backend { server 192.168.1.10:8080 weight3; server 192.168.1.11:8080 weight1; }如果业务状态存储在本地会话session里又没有统一缓存就需要ip_hash让同一个客户端的请求始终打到同一台后端机器upstream backend { ip_hash; server 192.168.1.10:8080; server 192.168.1.11:8080; }另外还有least_conn策略把请求发给当前活跃连接数最少的后端适合长连接和请求处理时间差异较大的场景。3.2 upstream配置实操实际项目里我通常会把upstream单独放在一个配置片段通过include加载。这样后端扩容时不用翻动主配置。下面是一个相对完整的示例upstream backend_api { least_conn; server 10.0.0.11:8080 max_fails3 fail_timeout30s; server 10.0.0.12:8080 max_fails3 fail_timeout30s; keepalive 32; } server { listen 443 ssl; server_name api.example.com; location /api/ { proxy_pass http://backend_api; proxy_http_version 1.1; proxy_set_header Connection ; } }keepalive 32表示Nginx向后端保持的空闲长连接数量能减少频繁建连的开销。配合proxy_http_version 1.1和清空Connection头性能会有明显提升这个优化在压测中能看到较大差异。3.3 健康检查与故障转移的经验max_fails和fail_timeout是故障转移的关键参数。max_fails3表示在fail_timeout时间内如果请求后端失败3次就将该后端标记为不可用之后Nginx会暂时不往这台机器发请求直到时间窗口过了再试探。这样做能自动摘掉故障节点但要注意默认的健康检查是主动请求失败才算。如果后端进程还在但接口响应极慢导致超时也会被算作失败。超时时间相关参数有proxy_connect_timeout、proxy_read_timeout和proxy_send_timeout要根据业务接口实际耗时合理设置别把正常的慢接口误判成故障。3.4 Nginx与TongWeb等国产中间件配合国内很多政务、金融项目会用到TongWeb这类应用服务器。Nginx对它的代理方式和对Tomcat并没有本质区别只要TongWeb监听在HTTP端口Nginx配置反向代理即可。需要注意两点一是TongWeb默认可能有自己的访问日志和超时设置代理层超时时间要大于应用层二是如果TongWeb开启了Session管理反向代理同样建议使用ip_hash策略避免Session跳变。4. 高频场景三静态资源托管与前端项目部署Nginx在最开始就是为解决静态文件高效传输而生的所以把它用作静态资源服务器也是极其常见且推荐的做法。前端打包后的dist目录、图片资源、下载文件都可以直接交给Nginx托管。4.1 单页应用部署必须处理的history路由问题Vue、React这类单页应用开发时用的是history模式路由地址是/user/123这种形式。但服务器上没有/user/123这个物理文件如果Nginx按磁盘路径找就会返回404。所以部署时必须在Nginx配置里加一行回退规则location / { root /data/www/myapp; index index.html; try_files $uri $uri/ /index.html; }try_files的意思是先找当前文件不存在就找当前目录再找不到就统一返回index.html。这样前端路由就能接管地址并渲染对应页面。千万不能漏掉这一行否则刷新子页面就是白屏或者404。4.2 用Nginx托管Vue3项目的标准配置一个较完整的Vue3项目部署配置如下server { listen 80; server_name example.com; gzip on; gzip_types text/plain text/css application/javascript application/json image/svgxml; gzip_min_length 1k; location / { root /opt/vue3-project/dist; index index.html; try_files $uri $uri/ /index.html; } location /assets/ { root /opt/vue3-project/dist; expires 30d; add_header Cache-Control public, immutable; } location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }静态资源分支设了30天强缓存是因为打包后的assets文件名通常带hash内容变了文件名就变不用担心缓存不更新的问题。而index.html不能设长缓存否则发版后用户还在看旧页面。4.3 多项目共享一个Nginx的挂载方式一台服务器上部署多个前端项目很常见。推荐给每个项目建独立配置放在/etc/nginx/conf.d/下通过不同server_name或者不同location路径区分。如果通过路径区分例如/admin/走管理后台/portal/走门户则需要小心配置root和try_fileslocation /admin/ { alias /opt/admin-project/dist/; try_files $uri $uri/ /admin/index.html; }alias和root的区别是经典坑root会把完整URI拼到路径后面alias则会把location匹配的部分替换掉。用错了通常表现为CSS或JS文件404因为找到的文件路径不符合预期。4.4 静态资源缓存策略与优化生产环境里图片和静态文件的缓存能显著减轻后端压力。配置expires 7d;或expires 30d;是最省事的办法Nginx会自动生成Cache-Control和Expires头。更精细的可以用add_header Cache-Control public, max-age31536000, immutable;。缓存策略的取舍依据是文件是否含hash指纹含hash的可以无限期缓存不含hash的文件建议只做协商缓存不要直接设长时间强缓存。5. nginx.conf配置文件逐层拆解一个大纲看懂全部几乎所有搜“Nginx配置”的人都会看到一份长长的nginx.conf每个参数都是什么意思、会不会互相冲突是新手最头疼的部分。这里我按层级把它拆开讲。5.1 全局块、events块、http块nginx.conf大致分成三层。最外面是全局块设置运行用户、worker_processes、错误日志路径等。接下来是events块负责连接处理模型常用的参数是worker_connections表示每个worker最多同时保持的连接数。http块是核心所有Web请求相关的指令都在这层include引用其他配置文件、default_type、log_format、sendfile、keepalive_timeout、gzip等都放在这里。http里嵌套serverserver里嵌套location这就是Nginx配置的基本骨架。5.2 server块和location块的匹配规则server块代表一个虚拟主机可以按端口区分也可以按域名区分。location则负责URI匹配。匹配规则里表示精确匹配^~表示不以正则方式匹配的前缀匹配~和~*表示区分大小写与不区分大小写的正则匹配普通字符串是最宽松的前缀匹配。Nginx的匹配优先级是精确匹配优先其次是最长前缀匹配且带^~再往后是正则匹配最后才是普通前缀匹配。很多人配接口转发时location /api/怎么都匹配不到就是因为前面某条正则把请求截胡了。遇到这类问题先检查是否存在优先级更高的location规则。5.3 常用参数的建议值worker_processes一般设成CPU核数虚拟机上也可以设为auto。worker_connections默认1024如果并发大可以调成4096或更高但受系统文件描述符限制需要同步修改ulimit。keepalive_timeout设置客户端长连接超时一般60s够用。gzip对文本类响应收益很大建议开启。sendfile默认开启能减少文件复制次数提升静态文件发送效率。client_max_body_size根据业务上送大小调整很多上传需求都需要动它。5.4 端口修改与多端口场景默认监听80端口但实际环境常常需要改端口。修改只需要找到server块里的listen 80;把80改成目标端口即可。Windows服务器上改端口也是同样逻辑区别在于Windows下配置文件路径一般是Nginx安装目录下的conf/nginx.conf。多端口场景下可以在一个http块里写多个server块分别listen不同端口对应不同项目或不同协议。使用server_name区分域名时同一个端口可以承载多个虚拟主机。6. 从启动到排障日常运维的高频操作清单6.1 Linux下的启动、停止与重载命令编译安装或包管理安装的Nginx常见操作如下nginx # 启动 nginx -s stop # 快速停止 nginx -s quit # 优雅停止等请求处理完再退 nginx -s reload # 平滑重载配置 nginx -t # 测试配置是否正确 nginx -V # 查看编译参数和模块很多人改了配置文件直接nginx -s reload但如果配置写错了reload可能失败甚至导致服务异常。正确姿势是先执行nginx -t确认配置文件语法无误再reload。使用systemd的Linux发行版可以用systemctl start nginx、systemctl enable nginx、systemctl reload nginx等命令。Ubuntu 16.04之前用init.d脚本较多现在Ubuntu 16.04基本都是systemd体系。如果服务器上同时存在两种管理方式优先使用systemctl。6.2 Windows环境下的部署细节Windows下部署Nginx比Linux简单从官网下载zip包解压即可。注意Windows安装包目录里没有nginx系统服务的概念都是以命令行方式启动。start nginx启动后可能看起来像“卡住”了其实是程序在前台正常运行。修改配置后用nginx -s reload重载停止则用nginx -s quit。Windows server 2016这类老系统上需要注意防火墙必须放行对应端口否则外部访问不通。在Windows部署Vue3项目时路径分隔符、权限问题都要留意不要使用中文路径避免编码问题导致静态资源加载不出来。6.3 用Docker部署Nginx并挂载多个项目Docker部署Nginx是目前很流行也较干净的方式。基础命令大概是这样docker pull nginx:1.21.5 docker run -d --name nginx-web \ -p 80:80 -p 443:443 \ -v /opt/www:/usr/share/nginx/html \ -v /opt/nginx/conf:/etc/nginx \ -v /opt/nginx/logs:/var/log/nginx \ nginx:1.21.5挂载的宿主机目录可以按项目划分然后在/opt/nginx/conf/conf.d/下写各项目的server配置。如果要把多个项目挂到同一个容器只需要在location里把root指向不同的宿主机挂载路径即可。使用Docker时宿主机文件改动会实时同步到容器内改完配置后执行docker exec nginx-web nginx -s reload生效。容器里的Nginx默认配置路径在/etc/nginx/下面主配置文件是/etc/nginx/nginx.conf日志在/var/log/nginx/。如果只是临时调试可以用-e传环境变量但生产环境建议把配置文件和日志目录都挂载出来便于维护。6.4 日志路径与排查技巧Nginx日志主要有access.log和error.log。Linux发行版默认在/var/log/nginx/下Windows编译包通常在logs/目录Docker容器内则在/var/log/nginx/。排查问题时我最先做的事情永远是看error.log。很多看起来像“后端挂了”的情况其实日志里写着“Connection refused”或“upstream timed out”。access.log则能看出请求到了哪一步、状态码是多少、耗时多久结构化分析时可以按$request_time字段排序找出最慢的请求。如果默认日志格式满足不了在http块里自定义log_formatlog_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for $request_time;修改后可以按$request_time分析慢接口再决定是优化后端还是调整Nginx超时参数。6.5 SSL证书与私钥类型的坑配置HTTPS的时候ssl_certificate对应的是证书文件ssl_certificate_key对应私钥文件。Nginx支持PEM格式的证书和私钥常见的是RSA私钥也有ECDSA私钥。如果你拿到的是.key或.pem文本文件检查开头有没有BEGIN PRIVATE KEY或BEGIN RSA PRIVATE KEY字样。确认格式没有问题后再检查证书链是否完整。现实里私钥报错大多不是格式不支持而是证书与私钥不匹配或者私钥文件权限太大Nginx的主进程拒绝读取。把私钥权限调整为600、属主设为运行Nginx的用户基本能解决。7. 值得长期保留的几条避坑建议最后说几条我在实际项目里总结的经验不算高深但每次都能救命。第一配置文件改动前先备份。我习惯在nginx.conf旁边保留一份nginx.conf.bak改出问题能快速回滚。第二生产环境尽量少用root方式直接跑Nginx。编译安装时指定--usernginx或者用包管理安装默认创建的nginx用户能避免很多越权问题。第三反向代理到后端时超时时间要分业务设置。管理后台的导入功能可能要几分钟如果代理层60s就掐断后端还在处理用户就会看到报错。遇到这类需求单独给对应location加proxy_read_timeout 300s;不要一刀切。第四日志分析要有目标。access.log文件会非常大不要直接打开翻末尾用tail -f实时跟踪或结合grep、awk统计特定接口的状态码分布。第五Nginx版本不是越新越好。新版本功能多但也可能有未知兼容问题。稳定版就好我用过的1.18和1.21在生产环境都很稳妥。把Nginx理解成一个聪明的流量调度员很多架构问题会豁然开朗。它负责收口、分发、缓存、加密让后端专注业务逻辑。无论是单机小项目还是集群大应用只要把Nginx这层用好性能和稳定性都会上一个台阶。踩过的坑、走过的弯路最终都会沉淀成一份更稳的配置和一套更顺的排查流程。
返回列表