【Nginx】性能优化与实战(上)
本文详细介绍了Nginx的KeepAlive、反向代理、Gzip压缩等核心功能的配置要点。KeepAlive部分讲解了连接复用、超时设置及请求数限制;反向代理部分解析了连接超时、缓冲机制及Header设置;Gzip压缩部分说明了压缩类型选择、压缩级别及缓存控制。最后提供了前端部署案例和SSI技术的应用场景分析。文章重点阐述了各项配置的原理、使用场景及参数调优建议,帮助开发者根据实际需求进行合理的Nginx性能优化。
一、KeepAlive
1.使用场景
- 可预知的连续操作:比如加载页面HTML后马上去请求JS和CSS资源等
- 减少连接开销:通过复用连接,降低服务器的CPU和网络延迟
2.客户端使用
keepalive_timeout timeout [header_timeout];
作用:设置一个KeepAlive连接在被服务器关闭之前,最长的空闲等待时间。
timeout
:指定了空闲超时时间,默认为75秒。如果设置为0,则表示禁用KeepAlive。header_timeout
(可选):在响应头Keep-Alive: timeout=time
中设置一个值,这个值可以被某些浏览器识别和使用。示例:
keepalive_timeout 65 65;
表示服务器空闲65秒后关闭连接,并通知浏览器连接的超时时间也是65秒。
keepalive_requests number;
作用:定义了单个KeepAlive连接上可以处理的最大请求数量。当请求数达到这个阈值后,连接将被关闭。
默认值:1000。
keepalive_time time;
作用:设置一个KeepAlive连接总的存活时长。无论连接是否活跃,只要从建立开始超过了这个时间,就会被强制关闭。这是一个较新的指令(Nginx 1.19.10+)。
示例:
keepalive_time 1h;
。
send_timeout time;
作用:设置向客户端发送响应的超时时间。这个超时不是指整个响应的传输时间,而是指两次连续的Nginx向Browser写数据操作之间的最大间隔时间。
重要陷阱:如果Nginx后面连接的应用有一个耗时很长的同步操作(例如超过60秒),在操作完成前Nginx没有向客户端发送任何数据,那么这个连接可能会因为
send_timeout
而被Nginx断开,导致用户收到错误。
3.上游服务器使用
在server或者location中:
proxy_http_version 1.1;
作用:必须将代理请求的HTTP协议版本设置为1.1。因为HTTP/1.1默认支持KeepAlive,而HTTP/1.0需要显式声明
Connection: keep-alive
头。
proxy_set_header Connection "";
作用:清除从客户端请求中继承来的
Connection
头。如果客户端的请求头是Connection: close
,这个头默认会传递给后端服务器,导致后端用完就关闭连接。将其设置为空字符串,可以避免这种情况,从而让Nginx来管理与后端的连接状态。
在upstream中:
keepalive connections;
作用:核心指令。定义了每个worker进程缓存的到上游服务器的最大空闲连接数。当有新的请求需要代理时,Nginx会优先从这些空闲连接中选取一个来使用,而不是新建连接。这个值不宜过大,但也不能太小,需要根据并发量和后端服务器的处理能力来设定。
总连接数:系统总的空闲长连接数是
keepalive
的值乘以worker_processes
的数量。例如,worker_processes 4;
和keepalive 128;
意味着Nginx最多会保持4 * 128 = 512
个到后端的空闲连接。你需要确保后端服务器(Tomcat等)的最大连接数设置能够承受这个数量。
二、反向代理
1.连接与超时指令
这些指令控制Nginx和上游服务器之间连接的生命周期和容错能力
proxy_connect_timeout
作用:定义Nginx与上游服务器建立TCP连接的超时时间。
为什么要设置:这是实现“快速失败”(Fast-Fail)机制的关键。如果某个后端服务已经宕机或网络不通,Nginx不必长时间等待,而可以在这个设定的时间内快速判断连接失败,然后根据策略(例如
proxy_next_upstream
)尝试连接下一个后端服务。这可以有效防止请求在无效的后端服务上积压,提升系统的可用性。好比:打电话时,拨号后等待对方“接听”的最长时间。如果一直没人接,你就会挂断。
proxy_send_timeout
作用:定义Nginx向后端服务发送请求数据时,两次连续写入操作之间的超时时间。请注意,它不是发送整个请求的总耗时。
为什么要设置:用于防止与一个“假死”的后端服务建立连接后,在发送数据时卡住。比如,后端服务的接收缓冲区满了,无法再接收新的数据,此时Nginx的发送操作就会阻塞,这个指令可以防止无限期阻塞。
好比:你给朋友发一个大文件,发送过程中,如果对方的网络突然卡住,导致你的发送进度条长时间不动,这个指令就会触发超时,中断发送。
proxy_read_timeout
作用:定义Nginx从后端服务读取响应数据时,两次连续读取操作之间的超时时间。
为什么要设置:这是最常用也是最重要的超时设置之一。它用于防止后端服务处理请求时间过长。如果后端因为复杂的业务逻辑、慢SQL或者死循环而长时间没有返回任何数据,Nginx会在等待超过这个时间后主动断开连接,并向客户端返回504 Gateway Time-out错误。这可以避免客户端请求被无限期挂起。
好比:你问朋友一个问题,等待他回答的最长时间。如果他一直沉默不语,超过了你的耐心限度,你就会不等了直接走开(返回504)。
2.代理缓冲指令
proxy_request_buffering
作用:决定是否启用客户端请求体的缓冲。
工作模式:
on
(默认):Nginx会完整接收客户端发送过来的全部请求体(比如POST的JSON数据、上传的文件),将其存放在内存或临时文件中,然后再一次性地将完整请求发送给后端服务。off
:Nginx会边接收客户端的数据,边向上游服务器转发,实现“流式”传输。
为什么要这样设计:默认开启缓冲,可以大大减轻后端服务的压力。后端服务可以一次性收到一个完整的、干净的HTTP请求,处理完后立即释放连接,而不必去适应客户端可能很慢的网络速度。只有在上传超大文件等特定场景下,为了避免Nginx磁盘I/O成为瓶颈,才会考虑关闭它。
proxy_buffering
作用:这是总开关,决定是否启用对上游服务器响应的缓冲。
工作模式:
on
(默认):Nginx会尽可能完整地接收后端服务返回的所有响应数据,存放在自己的缓冲区(内存或临时文件)中,然后再开始向客户端发送。off
:Nginx会边接收后端的数据,边同步地转发给客户端。
为什么要这样设计:同样是为了解耦和保护后端。Nginx可以迅速接收完后端的数据,让后端服务“功成身退”,去处理其他请求。然后Nginx再根据客户端的网速慢慢地将数据发送给客户端,整个过程后端服务无需参与。这极大地提高了后端服务的吞吐能力。
当proxy_buffering on;时,以下指令共同定义缓冲区的行为:
proxy_buffer_size
作用:设置用于存储上游响应第一部分(主要是HTTP响应头)的缓冲区大小。这块缓冲区通常不需要很大,但必须足够大以容纳所有的响应头。
proxy_buffers
作用:设置用于存储上游响应**主体(body)**的缓冲区。它由缓冲区数量和每个缓冲区的大小两部分组成。例如,
proxy_buffers 32 128k;
表示Nginx会为每个连接分配32个128KB的内存块用于缓冲响应体,总大小为32 * 128KB = 4MB
。
proxy_max_temp_file_size
作用:定义了这个临时文件的最大尺寸。如果响应数据连这个临时文件都装不下,Nginx会放弃缓冲,直接同步转发后续数据(如果可能),或者报错。
proxy_temp_path
作用:指定用于存放这些临时文件的目录路径。
levels
参数可以创建分级子目录,避免单个目录下文件过多影响性能。
proxy_temp_file_write_size
作用:设置Nginx在向临时文件写入数据时,每次写入的数据块大小。
3.Header设置指令
proxy_set_header
作用:允许你修改或添加从Nginx发往上游服务器的请求头。
为什么要设置:后端服务默认只能看到请求是来自Nginx的IP地址。为了让后端应用能获取到真实的客户端信息(如IP、域名、协议等),必须通过这个指令手动将这些信息添加到转发的请求头中。
最常见的配置:
proxy_set_header Host $host; # 传递原始请求的域名
proxy_set_header X-Real-IP $remote_addr; # 传递客户端真实IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加代理IP链
proxy_set_header X-Forwarded-Proto $scheme; # 传递原始请求的协议 (http/https)
三、Gzip
1.指令解析
gzip on|off;
作用:Gzip功能的总开关。默认是关闭的(
off
)。用法:要使用Gzip,必须首先设置为
on
。
gzip_types <mime_type> ...;
作用:指定需要进行Gzip压缩的文件MIME类型。默认只压缩
text/html
。为什么要设置:这是Gzip配置中最关键的一步。我们应该只对文本类文件进行压缩。像图片(JPG/PNG)、视频、PDF以及二进制文件通常不应该被Gzip,因为它们本身已经是高度压缩的格式,再次压缩不仅浪费CPU资源,有时甚至可能让文件体积变得更大。
示例:
gzip_types text/plain application/javascript text/css application/json text/xml;
。
gzip_comp_level <level>;
作用:设置Gzip的压缩等级,范围是1到9。
权衡:数字越大,压缩比越高(文件被压缩得更小),但消耗的CPU资源也越多。数字越小,压缩比越低,但处理速度更快。通常,设置为4-6是一个比较理想的平衡点,可以在可接受的CPU开销下获得不错的压缩效果。
gzip_min_length <length>;
作用:设置允许压缩的页面的最小字节数。只有当响应体的
Content-Length
大于这个值时,Nginx才会对其进行压缩。为什么要设置:对于非常小的文件,Gzip压缩本身带来的开销(CPU计算、添加HTTP头等)可能会超过其节省的带宽。设置一个合理的阈值(如256字节或1k)可以避免对小文件进行无效压缩。
gzip_proxied <off|any|...>;
作用:当Nginx作为反向代理服务器时,根据后端服务器返回的响应头来决定是否对响应进行压缩。
场景:比如后端服务可能已经对某些内容进行了压缩,或者通过
Cache-Control
头指定了不希望被代理缓存或修改。这个指令让Nginx可以更智能地处理这些来自上游的响应。常用值:
off
: 不对代理响应进行压缩。any
: 无条件对所有代理响应进行压缩。auth
: 当响应头中包含Authorization
时才压缩。expired
,no-cache
,no-store
,private
: 当响应头中包含相应的Expires
或Cache-Control
指令时,启用压缩。
gzip_vary on|off;
作用:在响应头中添加
Vary: Accept-Encoding
。为什么要设置:这个头非常重要。它告诉中间的代理缓存服务器(如CDN、公司的缓存代理),对于同一个URL,需要根据客户端的
Accept-Encoding
头来缓存不同的版本(一个压缩版,一个未压缩版)。如果没有这个头,缓存服务器可能会错误地把一个压缩版本返回给一个不支持Gzip的旧版浏览器,导致页面乱码。
gzip_buffers <number size>;
作用:设置用于存储Gzip压缩结果的缓冲区数量和大小。例如
gzip_buffers 16 8k;
表示使用16个8KB的内存块。
gzip_http_version <1.0|1.1>;
作用:启用Gzip所需的最低HTTP协议版本。默认是
1.1
,通常保持这个设置即可。
gzip_disable "MSIE [1-6]\.";
作用:通过正则表达式匹配客户端的
User-Agent
,对特定的浏览器禁用Gzip功能。为什么要设置:这是一个历史遗留选项,主要用于兼容一些古老的、对Gzip支持有问题的浏览器(如IE6)。
四、案例部署
1.前端文件存放
2.修改配置文件
在nginx.conf主配置文件添加模块化语句
vim /usr/local/nginx/conf/conf.d/nginx_demo.conf
在nginx_demo.conf里面填写下面的配置代码
server {# 1. 监听端口listen 80;# 2. 绑定的域名,可以是IP地址或 localhostserver_name hutao; # 或者填写您服务器的 IP 地址# 3. 前端项目静态文件根目录root /usr/local/nginx/html/dist;# 4. 默认首页文件index index.html index.htm;# 5. 前端路由与静态资源处理location / {try_files $uri $uri/ /index.html;}# 6. 后端 API 接口反向代理location /api/ {# 代理的目标地址# 当 proxy_pass 的地址带有 URI 路径时 (例如末尾有 /): Nginx 会将 location 匹配的路径部分剔除,然后将剩余部分拼接到 proxy_pass 的地址上。# 当 proxy_pass 的地址不带 URI 路径时 (例如末尾没有 /): Nginx 会将 location 匹配的完整路径直接拼接到 proxy_pass 的地址上proxy_pass http://127.0.0.1:8090;# 7. 设置请求头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;# 8. (可选) 增加超时时间proxy_connect_timeout 60s;proxy_read_timeout 60s;}# 9. (可选) 错误页面配置error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/local/nginx/html; # Nginx默认错误页面的存放路径}
}
3./的注意事项
五、SSI
1.基本介绍
理解为就是将多个HTML文件拼接到一起,变成一个逻辑上的HTML页面。如何不是对性能追求极致,个人认为用不上它,但是如果请求数量比较多的时候还是有用的。
2.总结
如果您在做一个纯粹的、内部使用的、不需要被搜索引擎收录的应用,或者是一个现代的单页面应用(SPA),那么使用 JavaScript 动态加载组件是完全正确且高效的。
但如果您在做一个面向公众的、内容型的、SEO 和用户体验至关重要的网站,那么使用 Nginx SSI 这种服务器端拼接技术,确保一次性给到客户端完整内容,就绝不是多此一举,而是保证项目成功的基石。
六、文件同步
如果使用SSI技术同时nginx多集群部署后,有可能会存在文件不一致情况,故需要设置文件同步策略。
yum install -y rsync
vim /etc/rsyncd.conf
# /etc/rsyncd.conf
# 全局配置
uid = root
gid = root
use chroot = no
max connections = 10
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid# 定义一个名为 'www' 的同步模块
[www]
path = /www/ # 数据实际存放的路径
read only = no # 设置为 no,允许客户端写入数据
auth users = sgg # 授权访问的用户名,可以自定义
secrets file = /etc/rsyncd.passwd # 存放用户名和密码的文件
# 格式: username:password
echo "sgg:111" > /etc/rsyncd.passwd
chmod 600 /etc/rsyncd.passwd
rsync --daemon
echo "111" > /etc/rsyncd.passwd.client
chmod 600 /etc/rsyncd.passwd.client
rsync -avz --delete --password-file=/etc/rsyncd.passwd.client /usr/local/nginx/html/ sgg@192.168.44.105::www
yum install -y automake
wget http://github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gz
tar -xvf inotify-tools-3.14.tar.gz
cd inotify-tools-3.14
./configure --prefix=/usr/local/inotify
make && make install
#!/bin/bash
MONITOR_DIR="/usr/local/nginx/html/"
TARGET_HOST="192.168.44.105"
TARGET_MODULE="www"
RSYNC_USER="sgg"
CLIENT_PASSWD_FILE="/etc/rsyncd.passwd.client"/usr/local/inotify/bin/inotifywait -mrq --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w%f %e' \
-e close_write,modify,delete,create,attrib,move ${MONITOR_DIR} | while read file
do
# 这里的 -az 等同于 -avz,但省略了 -v
rsync -az --delete --password-file=${CLIENT_PASSWD_FILE} ${MONITOR_DIR} ${RSYNC_USER}@${TARGET_HOST}::${TARGET_MODULE}
echo "${file} was synced to ${TARGET_HOST} at $(date)" >> /var/log/rsync_realtime.log
done
chmod +x realtime_sync.sh
nohup ./realtime_sync.sh & # 使用nohup让它在后台持续运行