Nginx性能优化

7272 字
36 分钟
Nginx性能优化

Nginx性能优化#

[TOC]


背景#

Terminal window
1、首先我们需要了解性能优化要考虑哪些方面
2、然后我们需要了解性能优化必要用到的压力测试工具ab
3、最后我们需要了解系统上有哪些注意和优化的点,以及nginx配置文件
我们再做性能优化工作前,我们重点需要考虑哪些方面,和了解哪些方面
1、首先需要了解我们当前系统的结构和瓶颈,了解当前使用的是什么,运行的是什么业务,都有哪些服务,了解每个服务最大能支撑多少并发。
比如nginx作为静态资源服务并发是多少,最高瓶颈在哪里,能支持多少QPS(每秒查询率)的访问请求,那我们怎么得出这组系统结构瓶颈呢,比如top查看系统的CPU负载、内存使用率、总得运行进程等,也可以通过日志去分析请求的情况,当然也可以通过我们前面介绍到的stub_statius模块查看当前的连接情况,也可以对线上的业务进行压力测试(低峰期),去了解当前这套系统能承担多少的请求和并发,已做好相应的评估。这个是我们做性能优化最先考虑的地方
2、其次我们需要了解业务模式,虽然我们是做性能优化,但每一个性能的优化都是为业务所提供的服务的,我们需要了解每个业务接口的类型,比如:电商网站中的抢购模式,这种情况下面,平时没什么流量,但到了抢购时间流量会突增。
我们还需要了解系统层次化的结构,比如:我们使用nginx做的是代理、还是动静分离、还是后端直接服务用户,那么这个就需要我们对每一层做好相应的梳理。以便更好的服务业务。
3、最后我们需要考虑性能与安全,往往注重了性能,但是忽略了安全。往往过于注重安全,对性能又会产生影响。比如:我们在设计防火墙功能时,检测过于严密,这样就会给性能带来影响。那么如果对于性能完全追求,却不顾服务的安全,这个也会造成很大的隐患,所以需要评估好两者的关系,把握好两者的孰重孰轻。以及整体的相关性,权衡好对应的点。

压力测试#

Terminal window
在系统业务量没有增长之前,我们就要做好相应的准备工作,以防患业务量突增带来的接口压力,所以对于接口压力测试就显得非常重要了,我们首先要评估好系统压力,然后使用工具检测当前系统情况,是否能满足对应压力的需求
[root@web01 ~]# ab
ab: wrong number of arguments
Usage: ab [options] [http[s]://]hostname[:port]/path
# ab命令属于httpd-tools
=======================
ab -n 200 -c 2 http://127.0.0.1/
#-n 要执行的请求数
#-c 请求的并发数
#-k 是否开启长连接
#后面接我们测试的链接(网址http://)
[root@web01 ~]# cd /etc/nginx/conf.d/
[root@web01 conf.d]# ll
total 28
-rw-r--r-- 1 root root 315 Mar 13 13:21 admin.conf
....
# 第一个子配置文件
[root@web01 conf.d]# mv test.conf 1.test.conf
# 我们把test放前面
[root@web01 conf.d]# ll
total 28
-rw-r--r-- 1 root root 129 Mar 11 09:24 1.test.conf
...
[root@web01 conf.d]# systemctl restart nginx
# 重启一下
[root@web01 conf.d]# curl 127.0.0.1
web01_index
# 再次拉取本地
[root@web01 conf.d]# curl http://127.0.0.1
web01_index
'上面两个是一样的'
[root@web01 conf.d]# ab -n20000 -c200 http://127.0.0.1
'报错了!后面应该跟具体的html页面才行!'
[root@web01 conf.d]# ab -n20000 -c200 http://127.0.0.1/index.html
This is ApacheBench, Version 2.3 <$Revision: 1874286 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
===============================
这些页面都是静态的,所以Nginx处理起来比较快!如果是动态的,就会Nginx比较鸡肋!
===============================
Benchmarking 127.0.0.1 (be patient)
Completed 2000 requests
Completed 4000 requests
Completed 6000 requests
Completed 8000 requests
Completed 10000 requests
Completed 12000 requests
Completed 14000 requests
Completed 16000 requests
Completed 18000 requests
Completed 20000 requests
Finished 20000 requests
# 上面是发的请求数
Server Software: nginx/1.26.1
# 服务器软件:Nginx,版本 1.26.1
Server Hostname: 127.0.0.1
# 测试的目标主机名(域名)
Server Port: 80
# 测试使用的端口(HTTP 默认端口 80)
Document Path: /index.html
# 请求的路径
Document Length: 12 bytes
# 服务器返回的内容长度
'测试参数与统计'
Concurrency Level: 200
# 并发请求数:200(模拟 200 个用户同时访问)
Time taken for tests: 0.739 seconds
# 完成所有测试请求的总耗时
Complete requests: 20000
# 成功的请求总数:2 万次
Failed requests: 0
✅# 失败的请求数:0(无失败)
Total transferred: 4840000 bytes
# 测试期间传输的总数据量
HTML transferred: 240000 bytes
# 仅 HTML 内容的总传输量
'性能指标'
Requests per second: 27050.24 [#/sec] (mean)
✅# 每秒处理的请求数(QPS),反映服务器吞吐量
Time per request: 7.394 [ms] (mean)
# 单个请求的平均耗时(并发模式下)
Time per request: 0.037 [ms] (mean, across all concurrent requests)
# 单个请求的实际平均耗时(不考虑并发)
Transfer rate: 6392.73 [Kbytes/sec] received
# 数据传输速率
'连接时间分布(单位:毫秒)'
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.6 1 4
Processing: 1 4 0.6 4 7
Waiting: 0 3 0.6 3 6
Total: 2 5 0.9 5 10
# 各项请求时间的统计(连接、处理、等待、总耗时):
'请求耗时百分比'
Percentage of the requests served within a certain time (ms)
50% 5
66% 5
75% 5
80% 5
90% 6
95% 6
98% 7
99% 8
# 99%的请求在8ms内完成
100% 10 (longest request)
'xx%的请求在xx毫秒内完成'

系统性能优化#

Terminal window
📌文件句柄,Linux一切皆文件
'也可以叫为 文件描述符 '
# 详细可以参考架构开篇,有介绍!!
文件句柄可以理解为就是一个索引,文件句柄会随着我们进程的调用-->频繁增加
系统为了安全和稳定,默认会限制每个用户/进程能打开的文件句柄数量
但在高并发场景(比如 Nginx、数据库、Java 应用),这个默认值远远不够!
所以我们要调大这个限制,但又不能无限大——要合理分配
====================================
🔄 举个生活化的例子:
想象你在餐厅打工:
文件句柄 = 你能同时端的盘子数量。
系统默认限制 = 老板说:“你最多只能端 10 个盘子!”(怕你摔了)
高并发服务 = 高峰期来了 100 个客人,你得端 50 个盘子!
修改 limits.conf = 你跟老板申请:“给我配个推车,我能端 65535 个!”
[root@web02 ~]# tail -1 /etc/security/limits.conf
* - nofile 65535
Nginx rlimit = 你自己偷偷练了杂技,哪怕老板没同意,你也先尝试多端几个(但最多不能超过老板允许的上限)
# 针对nginx进程,nginx自带配置
worker_rlimit_nofile
====================================
# 其他了解
1、全局修改(对所有用户生效)
* soft nofile 25535
* hard nofile 25535
==========================
* 表示所有用户
soft 是“软限制”:超过时只是警告,程序还能继续用(但可能失败)
hard 是“硬限制”:绝对不能超过
nofile = 最大打开文件数
2.针对特定用户(比如 root)
root soft nofile 65535
root hard nofile 65535
实际生产中,很多服务会用专用用户(如 nginx、mysql),也可以单独为它们设限
3.调整内核参数:
# (端口重用)
[root@web01 ROOT]# vim /etc/sysctl.conf
net.ipv4.tcp_tw_reuse = 1
# 开启端口复用
net.ipv4.tcp_timestamps = 0
# 禁用时间戳
'刚开始进公司,内核的东西先不要着急动!'
===================
net.ipv4.ip_forward=0
# 这个是内核转发
[root@web01 ROOT]# sysctl -p
# 可以查看我们添加的内核参数
# 配置文件里面有的东西
[root@web01 ROOT]# sysctl -a
# 可以查看所有内核参数
====================================
场景还原:
客户端每秒发起几千甚至上万个 HTTP 请求(短连接)
服务器处理很快,比如 10ms 就返回结果,然后主动关闭连接
每次关闭都会让服务器本地的一个端口进入 TIME_WAIT 状态,持续 60
# 什么是 TIME_WAIT?
'服务器处理完请求后主动close,TCP协议会进入一个叫TIME_WAIT的状态'
问题来了:服务器作为主动关闭方,会占用大量本地端口
结果:新的连接无法分配本地端口,客户端连不上或连接超时
# “服务器干正经事 1 秒,端口却要‘躺尸’60 秒”
'资源利用率极低,端口成了瓶颈'
怎么解决?(简单提一下)
1.启用 SO_REUSEADDR net.ipv4.tcp_tw_reuse
允许内核在安全前提下重用 TIME_WAIT socket(需配合时间戳)
# 上面的端口复用
2.让客户端主动关闭连接
TIME_WAIT 转移到客户端
3.使用连接池 or 改成长连接
从根本上减少连接 创建/关闭 次数
长连接:一次连接建立后,反复用来传多个请求
连接不会频繁 创建/关闭,所以几乎不会产生大量TIME_WAIT

代理服务优化#

Terminal window
通常nginx作为代理服务,负责转发用户的请求,那么在转发的过程中建议开启HTTP长连接
# 用于减少握手次数,降低服务器损耗
'负载均衡lb01的优化:'
upstream webs {
server 172.16.1.7;
server 172.16.1.8;
keepalive 16;
# 负载均衡服务器(lb01)和后台web长连接
}
server {
...
location / {
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
# 如果web01崩了,不返回它
include proxy_set;
# 文件位置:/etc/nginx/proxy_set
}
}
[root@lb01 nginx]# ll /etc/nginx/proxy_set
-rw-r--r-- 1 root root 258 Feb 22 10:18 /etc/nginx/proxy_set
[root@lb01 nginx]# cat /etc/nginx/proxy_set
proxy_set_header Host $http_host;
# 保留用户请求的Host主机
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 记录客户真实的IP地址
proxy_http_version 1.1;
# 固定HTTP版本为1.1,长连接
# 下面的超时控制参数,请参考Nginx04的文档!
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 4 128k;

静态资源优化#

image-20260302053546178
image-20260302053546178

Terminal window
静态资源优化
Nginx善于处理静态资源
# 要么从本地磁盘去拿,要么去NFS去拿
=========================
浏览器渲染:HTML、CSS、JS
图片文件:JPG、GIF、PNG
视频文件:Mp4、AVI
其他文件:TXT、DOC、PDF

image-20260320081335118
image-20260320081335118

image-20260320081611871
image-20260320081611871

  • 我们也可以通过 access.log 访问日志中,看出来请求明显增加!

  • 如果浏览器没有缓存,web服务器也没有设置

    那么每次用户访问页面或请求资源时,都会向后端Web服务器发起新的HTTP请求

Caution
  • 浏览器怎么知道,要走 缓存 还是 源站

  • 走缓存的时候,如果 后台web服务器 有改动怎么办?

  • 通过看头部信息!来确认是否改变!

    请求头响应头

image-20260320090202821
image-20260320090202821

image-20260320090706761
image-20260320090706761

概念谁发出?
Request(请求)客户端(浏览器等)
Response(响应)服务器
头部作用
ETag服务器为资源生成的唯一标识,用于精确比对资源是否变化
Last-Modified资源最后修改的时间戳,用于时间比对
If-None-Match客户端在后续请求中携带,与服务器的 ETag 比较
If-Modified-Since客户端在后续请求中携带,与服务器的 Last-Modified 比较

⚠️ 注意:ETagLast-Modified服务器设置的响应头

  • 如果浏览器访问过! 就会从 从上次响应中

    • 把ETag、Last-Modified等都存入本地缓存

    • 作为下次请求时使用的凭证

  • 用于下次携带对比

    If-None-MatchIf-Modified-Since 携带的就是它俩

缓存流程详解#

①浏览器检查本地是否有缓存#

  1. 浏览器先查看本地是否已有该资源的缓存
  2. 如果有,再看这个缓存是否“有效”(即是否过期)
  • 缓存有效性判断依据:

    • 是否设置了 Cache-Control: max-age=...

    • 或者 Expires

❗ 如果缓存已过期 → 必须向服务器发起请求

②发送条件请求#

如果缓存已过期,浏览器先发送一个 带条件头部的请求,让服务器判断资源有没有变

✅ 优先级: 先看 ETag,再看 Last-Modified

具体步骤如下:

  1. 如果之前响应中有 ETag,且浏览器缓存了它
  • 浏览器会在新请求中带上:
If-None-Match: "69b0b3c8-c"
  • 服务器收到后,比较当前资源的 ETag 是否一致:

    • 相同 → 返回 304 Not Modified

    • 不同 → 返回 200 OK + 新内容


  1. 如果 ETag 不存在或无效(比如服务器没返回)
  • 浏览器检查是否有 Last-Modified 时间戳
  • 若有,则在请求中带上:
If-Modified-Since: Wed, 11 Mar 2026 00:14:00 GMT
  • 服务器比较当前文件的修改时间:
    • 没变 → 返回 304 Not Modified
    • 改变了 → 返回 200 OK + 新内容

  1. 如果两者都没有
  • 浏览器只能发起普通请求(无条件请求),服务器返回完整内容(200 OK

image-20260302053646221
image-20260302053646221

✅ 总结:正确顺序

步骤条件行为
1缓存未过期(max-age > 0)直接使用本地缓存,不发请求
2缓存过期发起条件请求
3ETag → 发送 If-None-Match服务器比对 ETag
4ETag → 发送 If-Modified-Since服务器比对时间
5都没有 → 发送普通请求服务器返回完整资源

缓存设置#

Terminal window
# 配置在浏览器的缓存时间为7天
[root@web01 conf.d]# vim test.conf
serve
listen 80;
server_name test.kpyun.com;
root /code/test;
location ~ .*\.(jpg|gif|png)$ {
# 如果有人访问我们jpg...结尾的文件
# .*任意字符 /.撬棍
expires 7d;
# 在浏览器缓存7天
}
location / {
index index.html;
}
}
[root@web01 conf.d]# cp 史迪奇.png /code/test/sdq.png
[root@web01 conf.d]# ll /code/test/
total 576
-rw-r--r-- 1 root root 12 Mar 11 08:16 index.html
-rw-r--r-- 1 www www 582512 Mar 10 18:55 sdq.png
[root@web02 conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@web02 conf.d]# systemctl restart nginx

image-20260320105607939
image-20260320105607939

在企业中,一般我们会把那些,不经常变的东西缓存一两年!!

  • 比如公司的 LOGO

image-20260320110223040
image-20260320110223040

比较 稳定的资源 我们才会去,配置缓存!

  • 经常 改动的 我们不会去配的!
Terminal window
'开发代码没有正式上线'
# 取消js、css、html等静态文件缓存
location ~ .*\.(js|css|html)$ {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}
location ~ .*\.(jpg|gif|png)$ {
expires 7d;
}
'图片进行缓存,js,css不进行缓存!'
[root@web01 conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@web01 conf.d]# systemctl restart nginx
[root@web01 conf.d]# tail -f /var/log/nginx/access.log
10.0.0.1 - - [20/Mar/2026:11:11:33 +0800] "GET / HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
10.0.0.1 - - [20/Mar/2026:11:11:36 +0800] "GET / HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
'每次都是请求的源站!'
==========================
10.0.0.1 - - [20/Mar/2026:08:59:23 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36" "-"
# 这个是之前的访问日志,都是304,资源命中!

image-20260320111350509
image-20260320111350509

文件高效传输#

图片变清晰
图片变清晰

  • 传统读取文件方式 VS sendfile读取文件方式

🎯 目标:把一个文件从硬盘传给客户端(比如浏览器)

🔹 一、传统方式(没有 sendfile)

✅ 步骤分解

  • 第一步:操作系统从硬盘读文件 → 放进「内核缓冲区」
  • 第二步:程序把内核缓冲区的数据复制到「用户缓冲区」
  • 第三步:程序再把用户缓冲区的数据复制回「内核缓冲区」(准备发送)
  • 第四步:通过 Socket 发给客户端

💡 注意:数据在「用户空间」和「内核空间」之间来回拷贝了 两次

❌ 问题:

  • CPU 要参与搬运,浪费性能
  • 对于大文件,特别慢

🔹 二、sendfile 方式(开启 sendfile)

📌 简单说:“直接让内核帮忙,不经过用户!”

✅ 步骤分解:

  • 第一步:操作系统从硬盘读文件 → 放进「内核缓冲区」
  • 第二步:直接告诉内核:“把这块内存发给 Socket”
  • 第三步:内核直接把数据从「内核缓冲区」发到网络

💡 数据只在内核空间流动,没有进入用户空间

✅ 好处:

  • 节省 CPU

  • 更快、更省资源,尤其适合大文件传输

🎯 所以, sendfile = 让内核代劳,你只管指挥,不费力气!

Terminal window
'sendfile'在主配置文件里面:
[root@web01 nginx]# ll /etc/nginx/nginx.conf
-rw-r--r-- 1 root root 699 Mar 10 17:44 /etc/nginx/nginx.conf
...
sendfile on;
# Nginx默认是开启的
====================================
tcp_nopush VS tcp_nodelay
'它俩默认都是off关闭状态!'
⚠️ 不推荐混用!
不要同时开启 tcp_nopush tcp_nodelay
它们是互斥的:
tcp_nopush = “别急,等我攒够了再发”
tcp_nodelay = “别等,有就发”
🎯 总结口诀
🟢 “一个请求 = 一个完整响应” nopush(吞吐优先)
🔴 “一个连接 = 多次来回对话” nodelay(延迟优先)
🎯 一句话定义
吞吐优先:
👉 “一次能运多少货?” —— 关注单位时间内传输的总数据量
延迟优先:
👉 “第一个字多久到?” —— 关注单次操作的响应速度
====================================
🎯 核心结论(你总结得非常到位):
不能单纯以“文件大小”来决定用 tcp_nopush 还是 tcp_nodelay,
在大多数高性能服务中(尤其是静态资源、API、CDN),tcp_nopush(吞吐优先)用得更多、更合理
核心真相:即使文件小,Nginx 也会“批量发送”
场景还原:用户打开一个网页
浏览器并发请求 10 个资源:index.html, style.css, app.js, logo.png, icon1.png, ...
每个文件可能只有 2KB~50KB
Nginx 在内核层面通过 sendfile + tcp_nopush,会把多个文件的数据“合并成一个大TCP包”发出去!
🧠 在一次响应中,把整个文件内容一次性高效发出
👉 可见:吞吐优先的场景远多于延迟优先,尤其在基础设施层
====================================
两者都是好工具,关键看业务需求:你是要“快”(响应快),还是要“多”(传得多)?
场景推荐配置为什么?
静态文件服务器(如 Nginx 传图片/JS/CSS)tcp_nopush on;批量发包,最大化吞吐
CDN 边缘节点tcp_nopush on;大量文件分发,追求效率
日志写入tcp_nopush on;日志是顺序批量写,适合攒包
SSH / 远程桌面 / 在线游戏tcp_nodelay on;用户按键必须立刻响应,不能等
金融交易系统tcp_nodelay on;毫秒级延迟决定成败
WebSocket / gRPC / HTTP/2tcp_nodelay on;实时双向通信,小消息频繁
日志收集(如 Fluentd → Kafka)tcp_nopush on;(如果批量发送) tcp_nodelay on;(如果实时上报)取决于是否缓冲日志
Telnet / 交互式 Shelltcp_nodelay on;每敲一个键都要立刻传

📊 实测数据参考(来自 Nginx 官方和社区压测)

配置QPS(10KB 静态文件)平均延迟CPU 使用率
sendfile on; tcp_nopush on;28,0008ms45%
sendfile on; tcp_nodelay on;22,0007ms60%

👉 虽然 nodelay 延迟略低 1ms,但 吞吐下降 20%+,CPU 更高 ——在 CDN/静态站 这种高并发场景,得不偿失!

静态资源压缩#

image-20260302053745371
image-20260302053745371

  • Nginx将 响应报文 发送至 客户端 之前启用压缩功能,然后进行传输

  • 这能够有效地节约带宽,并提高 响应 客户端的速度

级别CPU 消耗适用场景特点说明
1★ (~20%)实时流媒体速度优先,压缩较弱
3★★ (~35%)动态内容平衡速度与压缩
6★★★ (~50%)==通用 Web 内容==默认推荐,综合性能较好
9★★★★ (~55%)静态资源压缩最强,适合预处理资源
Terminal window
'内容冗余度会影响压缩效果'
高冗余内容(如重复文本、未压缩的日志、HTML/CSS/JSON):
1.含大量重复模式或空白字符
2.极易被压缩
低冗余内容(如加密数据、随机字符串、图片/视频):
1.几乎无重复模式
2.难以进一步压缩
建议:仅对文本类资源启用高压缩级别
图片、视频等媒体文件通常无需再用 Gzip 算法进行二次压缩
============================
gzip传输压缩
'传输前压缩,传输后解压'
# 默认是关闭的状态

image-20260320141023675
image-20260320141023675

Terminal window
'压缩图片效率较低'
# 别人的图片都是经过处理的!所占的空间的是kb
在企业中,图片这些东西让前端自己调!除非万不得已,压缩会占用我们服务器的资源!
=========================
案例: 压缩txt文件
[root@web01 nginx]# cat /etc/services /etc/services /etc/services > /code/test/hh.txt
[root@web01 nginx]# ls -lh /code/test/hh.txt
-rw-r--r-- 1 root root 2.0M Mar 20 14:07 /code/test/hh.txt
# 准备2M的文件
'浏览器测试访问大小'

image-20260320141322355
image-20260320141322355

Terminal window
'配置压缩'
[root@web01 conf.d]# cat 1.test.conf
server {
listen 80;
server_name test.kpyun.com;
root /code/test;
location / {
index index.html;
}
location ~ .*\.(js|css|html)$ {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}
location ~ .*\.(jpg|gif|png)$ {
expires 7d;
}
location ~ .*\.(txt|xml|html|json|js|css)$ {
# 如果匹配到这些
gzip on;
# 开启压缩
gzip_http_version 1.1;
# 压缩http版本1.1
gzip_comp_level 6;
# 压缩等级6(通用)
gzip_types text/plain text/css text/javascript application/javascript application/json application/xml;
# 压缩的类型:必须用空格隔开
gzip_min_length 1024;
# 小于1KB的文件不压缩(避免小文件压缩后反而变大)
}
}
[root@web01 conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@web01 conf.d]# systemctl restart nginx
'再次打开浏览器测试访问大小'

image-20260320143639880
image-20260320143639880

防止资源盗链#

Terminal window
防盗链:'指的是防止资源被其他网站恶意盗用'
===============================
# web02配置盗链、偷取WEB01的图片
[root@web02 ~]# vim /etc/nginx/conf.d/test.conf
server {
listen 80;
server_name daolian.com;
root /code/test;
location / {
index index.html;
}
}
# 域名:daolian.com
[root@web02 ~]# vim /code/test/index.html
<html>
<head>
<meta charset="utf-8">
<title>FROM web01 picture</title>
</head>
<body style="background-color:pink;">
<center><img src="http://test.kpyun.com/sdq.png"/></center>
</body>
</html>
'背景颜色是pink色!'
[root@web02 ~]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@web02 ~]# systemctl restart nginx
# 最后 windows的hosts解析
10.0.0.8 daolian.com
10.0.0.7 test.kpyun.com

image-20260320152032187
image-20260320152032187

Terminal window
'web01配置防盗链'
# 准备一张禁止盗图的图片
[root@web01 conf.d]# cat 1.test.conf
server {
listen 80;
server_name test.kpyun.com;
root /code/test;
location / {
index index.html;
}
location ~ .*\.(js|css|html)$ {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}
location ~ .*\.(jpg|gif|png)$ {
expires 7d;
valid_referers none blocked test.kpyun.com *.baidu.com;
# 有效的来源 它们使用空格分隔!!!
# none:请求没有带Referer(比如用户直接在浏览器地址栏输入图片链接)
# blocked:Referer存在,但不是以 http:// 或 https:// 开头(比如被防火墙/代理删掉了协议头)
# 后面是你自己的域名和信任的域名,允许他们请求
if ( $invalid_referer ) {
# 如果是无效的请求
# return 403;可以直接返回禁止
rewrite ^(.*)$ /d.png break;
# 或者跳转到/code/test/d.png,并停止匹配下面的location
}
}
location ~ .*\.(txt|xml|html|json|js|css)$ {
gzip on;
gzip_http_version 1.1;
gzip_comp_level 6;
gzip_types text/plain text/css text/javascript application/javascript application/json application/xml;
gzip_min_length 1024;
}
}
[root@web01 conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@web01 conf.d]# systemctl restart nginx
# 照片的准备!
[root@web01 conf.d]# ls -lh /code/test/d.png
-rw-r--r-- 1 root root 543K Mar 20 16:28 /code/test/d.png
'再次浏览器访问'
daolian.com

image-20260320163115127
image-20260320163115127

跨域访问#

image-20260302053911408
image-20260302053911408

🌐 什么是跨域访问?

想象一下,你(浏览器)在 A 餐厅(a.com)点了一份牛排 但你想加点 B 餐厅(b.com)特制的酱料

于是你对服务员说:“麻烦帮我从 B 餐厅拿点酱料过来”

但餐厅有个规定(同源策略):

“为了安全,A 餐厅不能随便帮你去别的餐厅拿东西,除非 B 餐厅明确同意”

所以,当你请求 B 餐厅的酱料时,B 餐厅必须在回信里写清楚:

“我允许 A 餐厅的客人来拿我的酱料”(也就是返回 Access-Control-Allow-Origin: https://a.com

如果你没看到这句话,你的浏览器(作为守规矩的食客)就会拒绝使用这份酱料,并报错:“跨域请求被阻止了!”


✅ 总结一下(一句话版):

  • 浏览器 出于安全,默认禁止网页向不同域名发请求(跨域)
  • 服务端(比如通过 Nginx)可以通过在响应头中添加 Access-Control-Allow-Origin 来“授权”哪些网站可以跨域访问
Terminal window
# WEB02准备跨域
[root@web02 ~]# cat /etc/nginx/conf.d/test.conf
server {
listen 80;
server_name kuayu.com;
root /code/test;
location / {
index index.html;
}
}
[root@web02 ~]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@web02 ~]# systemctl restart nginx
[root@web02 conf.d]# cat /code/test/index.html
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>测试跨域访问</title>
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
</head>
<script type="text/javascript">
$(document).ready(function(){
$.ajax({
type: "GET",
url: "http://test.kpyun.com",
success: function(data) {
alert("sucess 成功了!!!");
},
error: function() {
alert("fail!!,跨不过去啊,不让进去啊,只能...!");
}
});
});
</script>
<body>
<h1>跨域访问测试</h1>
</body>
</html>
=================================
上面html页面代码里面不能够带#号!
'浏览器测试访问'
windows的hosts解析
10.0.0.8 kuayu.com
10.0.0.7 test.kpyun.com

image-20260320165444718
image-20260320165444718

Terminal window
# 配置WEB01允许跨域请求
[root@web01 conf.d]# vim 1.test.conf
server {
listen 80;
server_name test.kpyun.com;
root /code/test;
location ~ .*\.(html|htm)$ {
add_header Access-Control-Allow-Origin http://kuayu.com;
# add_header Access-Control-Allow-Origin *;
# 这个是允许所有跨域(不推荐生产环境用)
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
}
}
[root@web01 conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@web01 conf.d]# systemctl restart nginx
'再次测试访问!'

image-20260320171447866
image-20260320171447866

CPU亲和#

image-20260302054120057
image-20260302054120057

Terminal window
没有 CPU 亲和性时(默认情况):
进程在不同CPU核心之间来回跳 ---> 频繁切换任务,效率低,还容易出错!
1.每次切换,CPU缓存(Cache)里的数据就失效了(cache miss)
2.CPU 得重新从内存加载数据,速度变慢,性能下降
==============================
启用 CPU 亲和性后:
给每个 Nginx worker 固定分配一个CPU核心
1.减少上下文切换和缓存失效
2.提高 CPU 利用率和请求处理速度
Terminal window
# 以web01举例
# 分配4个核心
[root@web01 conf.d]# lscpu | grep -i cpu\(s\) | sed -n '1p'
CPU(s): 4
[root@web01 conf.d]# systemctl restart nginx
[root@web01 conf.d]# ps aux | grep nginx
root 4765 0.0 0.1 33796 1124 ? Ss 17:40 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
# 主进程管理着下面4个worker
www 4766 0.0 0.4 45104 4428 ? S 17:40 0:00 nginx: worker process
www 4767 0.0 0.4 45104 4068 ? S 17:40 0:00 nginx: worker process
www 4768 0.0 0.4 45104 4068 ? S 17:40 0:00 nginx: worker process
www 4769 0.0 0.4 45104 4068 ? S 17:40 0:00 nginx: worker process
===================================
'查看默认的Nginx进程的绑定核心!'
[root@web01 conf.d]# ps -eo pid,args,psr|grep [n]ginx
4765 nginx: master process /usr/ 1
# 主进程
4766 nginx: worker process 2
4767 nginx: worker process 2
4768 nginx: worker process 0
4769 nginx: worker process 0
# 有一个CPU核心完全偷懒 3
# 完全是少一个核心的!
配置CPU亲和方式
# 最佳绑定方式,修改nginx启动的work进程为自动
[root@web01 ~]# grep -C 1 worker_cpu_affinity /etc/nginx/nginx.conf
worker_processes auto;
# 配置在它的下面就行(主配置文件)
worker_cpu_affinity auto;
# 配置CPU自动亲和
[root@web01 ~]# systemctl restart nginx
'再次查看cpu亲和'
[root@web01 ~]# ps -eo pid,args,psr|grep [n]ginx
4870 nginx: master process /usr/ 3
4871 nginx: worker process 0
4872 nginx: worker process 1
4873 nginx: worker process 2
4874 nginx: worker process 3
# CPU核心被均匀分配!

Nginx优化总结#

总并发连接 = worker_processes × worker_connections

worker_rlimit_nofile (fd 上限,从内核申请) ==每个 worker 独立== ↓ worker_connections (连接数上限,nginx 自己限)

一个请求最少 2 个 fd

worker_rlimit_nofile = 65535 ← 内核允许这个 worker 打开的最大 fd 数 worker_connections = 32768 ← nginx 允许自己最多维持 32768 个连接 ← 32768 × 2 = 65536 ≈ 65535 ✅

现在 65535 对单个 worker 来说刚刚好

一个 TCP 连接 ──可以承载──→ 多个 HTTP 请求 (keep-alive)

客户端 ──── TCP 连接 ────→ nginx ──── TCP 连接 ────→ 后端

Nginx FD 占用与连接/请求关系

阶段fd 占用
客户端建立 TCP 连接fd × 1
nginx 把请求代理到后端,建立上游连接fd × 1
之后该连接上的第 N 个请求fd 不增加(复用)
客户端断开,上游连接回连接池fd 释放

核心结论:

  • 2 个 fd 是按连接算,不是按请求算
  • 10000 并发连接 ≈ 20000 fd,但这 10000 连接可能同时处理着 50000 个请求(keep-alive 管道)
  • worker_connections 32768 的意思是同时存活 32768 个连接,而不是 32768 个请求——请求量一般远大于连接数
Terminal window
nginx优化总结,nginx通用优化配置文件
[root@nginx ~]# cat nginx.conf
user www; # nginx进程启动用户
worker_processes auto; # 与cpu核心一致即可
✅worker_cpu_affinity auto; # cpu亲和
error_log /var/log/nginx/error.log warn; # 错误日志
pid /run/nginx.pid;
✅worker_rlimit_nofile 65535; # 每个work能打开的文件描述符
# 每个 worker 独立
'可以参考上面的文件句柄!'
events {
use epoll; # 使用epoll高效网络模型(默认的)
# 可以不用配置
✅worker_connections 32768; # 每个worker进程可以同时处理最大连接数
# 一个连接平均占用两个文件描述符
}
http {
include mime.types;
default_type application/octet-stream;
charset utf-8,gbk; # 统一使用utf-8字符集
# 定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#定义json日志格式
log_format json_access '{"@timestamp":"$time_iso8601",'
'"host":"$server_addr",'
'"clientip":"$remote_addr",'
'"size":$body_bytes_sent,'
'"responsetime":$request_time,'
'"upstreamtime":"$upstream_response_time",'
'"upstreamhost":"$upstream_addr",'
'"http_host":"$host",'
'"url":"$uri",'
'"domain":"$host",'
'"xff":"$http_x_forwarded_for",'
'"referer":"$http_referer",'
'"status":"$status"}';
access_log /var/log/nginx/access.log main; # 访问日志
✅server_tokens off; # 禁止浏览器显示nginx版本号
✅client_max_body_size 20m; # 文件上传大小限制调整 默认1M
'php也有上传限制'
# 像论坛,博客...它们才需要进行修改!
# 文件高效传输,静态资源服务器建议打开
✅sendfile on;
✅tcp_nopush on;
# 文件实时传输,动态资源服务建议打开,需要打开keepalive
🔍tcp_nodelay on;
🔍keepalive_timeout 65;
# 如果改为0,就是短连接,请求一次断开一次
include /etc/nginx/conf.d/*.conf;
}

面试题: NGINX优化过什么

1、CPU亲和

2、调整每个worker进程的最大连接数、默认1024

3、文件的高效传输sendfile

4、开启tcp长连接,以及长连接超时时间keepalived

5、开启文件传输压缩gzip

6、开启静态文件expires缓存

7、隐藏nginx版本号

8、配置防盗链、以及跨域访问

9、优雅显示nginx错误页面

10、nginx加密传输https优化

11、防DDOS、cc攻击,限制单IP并发连接,以及http请求

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Nginx性能优化
https://www.kpyun.fun/posts/web/nginx/nginx07/
作者
久棹
发布于
2025-12-04
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
久棹
只要胆子大,天天寒暑假!
公告
欢迎来到久棹的技术小站!本站专注 Linux 运维学习笔记分享,如有问题欢迎交流探讨 🎉
分类
标签
站点统计
文章
98
分类
11
标签
203
总字数
244,453
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.5
文章许可
CC BY-NC-SA 4.0

文章目录