Nginx优化
# 服务性能优化介绍
# 服务优化前提
- 了解当前系统最高能承担多少请求和并发
- 一般在访问低峰期、或者下线时进行测试。
- 了解当前系统的状态,通过top查看系统的CPU负载、内存使用率、和总的运行进程等,也可以通过stub_status模块查看站点情况。
- 了解当前使用的服务,每个服务最大能支撑多大并发,QPS每秒查询率的访问请求最高瓶颈是多少。
- 了解业务的场景模式
- 了解流量的高峰期、低峰期等。
- 了解系统层次化结构
- 比如使用Nginx是用做代理、还是动静分离、还是后端服务等。
- 同时对每一层服务进行梳理,服务之间的调用关系,调用时的短板和问题等。
- 考虑性能和安全的平衡
# 影响性能的指标
- 网络
- 网络的流量。网络是否丢包,这些是否影响http的请求与调用。
- 系统
- 硬件有没有磁盘损坏、磁盘速率,系统负载、内存、系统稳定性。
- 服务
- 连接优化、请求优化,根据业务形态做对应的服务设置。
- 程序
- 接口性能、处理速度、程序执行效率,优化程序的效率一般是开发的职责。
- 数据库
- 数据库增删改查是否阻塞业务流程。
- SQL语句是否使用到索引。
# 根据OSI模型进行服务优化
- 硬件
- 代理服务要考虑CPU、静态资源要考虑磁盘IO、动态资源要考虑CPU、内存。
- 网络
- 按业务需求,越快越好。
- 系统
- 优化文件描述符。
- Linux中一切皆文件,读取打开文件、接收连接等都会占用描述符,系统默认是最大1024,一般是不够的所以需要优化。
- 可通过进程PID在/proc/[进程PID]/fd/中查看打开的文件描述符。
- 优化文件描述符。
- 应用
- 服务与服务之间保持长连接,比如开启
http1.1
。
- 服务与服务之间保持长连接,比如开启
- 服务
- 静态资源优化,比如增加缓存等。
- 动静分离,不同的服务处理不同的请求。
# ab压测工具
在系统业务量增长之前,我们就需要做好相应准备,以防压力徒增带来的接口压力,所以一般会提前进行压力测试,然后根据评估结果来拟定相应对策。
一般会先测压静态资源,再测压动态资源,以此找到系统的短板。
安装ab压测工具
yum install -y httpd-tools
1ab命令
ab -n 200 -c5 http://127.0.0.1/
-n
- 总的请求次数-c
- 并发请求数-k
- 是否开启长连接
压测信息解释
Document Path
- 测试页面路径Document Length
- 测试页面大小Concurrency Level
- 并发级别Time take for tests
- 完成请求总时间Complete requests
- 总请求次数Failed requests
- 请求失败次数Write errors
- 错误页面次数Total transferred
- 请求产生的总流量大小HTML transferred
- HTML页面传输的总大小Requests per second
- 每秒最多能处理的次数(重点需要关注的信息)Time per request
- 每一客户端的请求时间Time per request
- 每一请求的处理时间Transfer rate
- 传输的速率
# Nginx服务优化
# 加大文件描述符
文件描述符会随着我们进程调用的增加而增加,而系统默认的最大文件句柄数一般是不够使用的。因为作为服务端,TCP连接过多时连接文件会占用文件描述符资源,此时如果文件描述符触发了限制,就可能无法建立新的连接,所以一般会对文件描述符限制进行合理的调整。
文件描述符配置方法
# 编辑文件描述符限制配置文件 vim /etc/security/limits.conf # 在其中添加以下配置即可 # 全部用户修改限制 * soft nofile 65535 * hard nofile 65535 # 指定用户修改限制 nginx soft nofile 65535 nginx hard nofile 65535
1
2
3
4
5
6
7
8
9
10
11- 第一列为
nginx
表示针对nginx
用户进行限制,如果为*
则表示所有用户。 - 第二列为
soft
表示对单个服务软件最大文件描述符数进行限制。 - 第二列为
hard
表示对整个服务器硬件最大文件描述符数进行限制。 nofile [num]
表示定义限制的最大文件描述符数,文件描述符的数量限制应当根据内存大小来决定,文件描述符设的很大,但是内存不够也没有什么用,反而还可能内存溢出。
- 第一列为
相关命令
ulimit -a
- 显示当前所有的资源限制。lsof -p [PID]
- 查看进程打开的文件描述符。
# time_wait复用
作为TCP连接发起方时,如果 TIME_WAIT 状态过多,就可能会占满了所有端口资源,最终导致无法创建新连接。如果出现该问题,应当排查是异常原因引起还是正常原因导致的,如果只是单纯的正常连接很多,则我们可以通过调整内核参数来开启time_wait复用来进行优化。
调整内核参数
vim /etc/sysctl.conf # 在配置文件中添加 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_timestamps = 1
1
2
3
4
5- time_wait复用配置只对TCP连接发起方(客户端)有意义。
- 另外客户端和服务端都需要配置
net.ipv4.tcp_timestamps = 1
才有效。 - 调整完后通过
sysctl -p
检查即可。
# Nginx代理长连接优化
Nginx代理作为转发用户请求的媒介,可以通过设置keepalive长连接来减少握手次数、提高效率、减少资源消耗。
配置方式
upstream test { server 10.0.0.7:80; keepalive 32; # keepalive_requests 100; }
1
2
3
4
5keepalive 32
- 表示集群后端的每个Worker接收的最大长连接数。keepalive_requests 100
- 表示一次长连接的最大的请求次数,当请求数超过将会关闭连接。如果并发请求超过了该值,Nginx也会启动新的转发请求,新请求建立的也是长连接,但可能会造成大量的time_wait,需要使time_wait状态可复用。
# Nginx代理优化
常用且通用的配置参数可以保存到另外的文件,如proxy_params.conf,然后使用include导入,使配置文件看上去更简洁、可读。
配置方式
# 继承客户端请求的Host信息进行转发,访问正确的server_name(一般必加) proxy_set_header Host $http_host; # 日志显示访问用户IP(一般必加) proxy_set_header X-Forward-For $remote_addr; # 清除"connection"头字段 proxy_set_header Connection ""; # 代理连接web超时时间 proxy_connect_timeout 30s; # 代理等待web响应超时时间 proxy_read_timeout 60s; # web回传数据至代理超时时间 proxy_send_timeout 60s; # 开启代理缓冲区,web回传数据至缓冲区,代理边收边传返回给客户端 proxy_buffering on; # 代理接收web响应的头信息的缓冲区大小 proxy_buffer_size 32k; # 缓冲代理接收单个长连接内包含的web响应的数量和大小 proxy_buffers 4 128k;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# CPU亲和优化
CPU亲和可以减少进程之间的频繁切换,减少性能消耗。
配置前worker在需要处理任务时,由于CPU核心已经被其他worker占用了,而占用的worker又没有处于工作状态,那么要处理任务的worker就会抢占使用这个CPU核心,这样就会造成来回的切换,就会造成系统性能的消耗。
配置后CPU亲和会将CPU核心和worker进行进行绑定,将每个worker进程固定到对应的cpu上执行,这样就能提高CPU缓存命中率,获得更好的性能。
配置方式
# 主配置文件中添加配置 # 或者等于核心数的1-1.5倍 worker_processes auto; # CPU亲和,绑定CPU worker_cpu_affinity auto;
1
2
3
4
5
6查看CPU核心数
- lscpu命令
- 其中的CPU(s)就是总核心数。
- lscpu命令
# 静态资源缓存
设置浏览器缓存可提高网站性能,使用户第一次访问后,数据在用户的浏览器中长时间缓存,这样下次用户访问相同资源时,就可以直接使用浏览器本地的缓存了。
缓存原理
- 浏览器缓存机制是基于HTTP协议缓存机制实现的,它依赖于特殊的头信息来与服务器进行特殊的验证,如:Expires (本地缓存过期具体时间)、Cache-control (本地缓存过期时长,为0表示不缓存)
- 浏览器无缓存时
- 浏览器请求 --> 无缓存 --> 请求WEB服务器 --> 请求响应、协商 --> 呈现。
- 浏览器有缓存时
- 浏览器请求 --> 有缓存 --> 校验是否过期 --> 呈现。
浏览器过期校验检查机制
- 浏览器请求服务器会先在本地检查缓存中数据是否过期,如果没有过期则直接从缓存文件中取。
- 如果缓存过期,首先检查是否存在ETag,ETag类似于文件MD5加密的值。
- 如果ETag存在,则客户端会向web服务器携带if-None-Match头信息进行请求,然后服务器会将if-None-Match值与etag值进行比对,如果一致则表示缓存只是过期,而数据并没有改变,然后由服务器决策返回200还是304。
- 如果ETag不存在,则会进行last-Modified检查验证,last-Modified是缓存中文件最后修改时间。客户端会向web服务器携带if-Modified-Since头信息进行请求,然后服务器会将if-None-Match值与last-Modified值进行比对,如果一致则表示缓存只是过期,而数据并没有改变,然后由服务器决策返回200还是304。
静态资源配置缓存
# 如果请求的资源是静态资源则进行缓存 location ~ \.*(jpg|png|jpeg|gif)$ { expires 1d; }
1
2
3
4expires 1d;
- 缓存过期时长,h时、d天、m月、y年。
关闭缓存配置
add_header Cache-Control no-store; add_header Pragma no-cache;
1
2
# 静态资源读取
sendfile
sendfile参数用于提高静态资源的读取效率,在主配置文件中默认是开启的。
提高效率的原理
# 传统处理文件方式 # 应用读取磁盘的文件资源需要由内核调用,然后再到程序的用户空间中,然后再到内核空间,最后才由socket服务发送出去 文件 -> 内核空间 -> 用户空间 -> 内核空间 -> Socket # sendfile处理文件方式 # 只经过内核空间直接发出,大大提高读取效率 文件 -> 内核空间 -> Socket
1
2
3
4
5
6
7
tcp_nopush
tcp_nopush参数用于将多个包一次发送,提高大文件的网络传输效率,一般大文件或者静态资源传输建议打开,但建议不要在相同区域和tcp_nodelay一起开。
# 需要启用sendfile才能使用 Syntax: tcp_nopush on | off; Default: tcp_nopush off; Context: http,server,location
1
2
3
4配置方式
# 在需要使用的location中配置即可 location /doc { ... ... tcp_nopush on; }
1
2
3
4
5
tcp_nodelay
tcp_nodelay参数用于提高网络数据传输的实时性,一般实时性要求比较高的文件或者动态资源建议打开,但建议不要在相同区域和tcp_nopush一起开。
# 需要启用keepalive_timeout才能使用 Syntax: tcp_nopush on | off; Default: tcp_nopush off; Context: http,server,location
1
2
3
4配置方式
# 在需要使用的location中配置即可 location /video { ... ... tcp_nodelay on; }
1
2
3
4
5
# 静态资源压缩
压缩静态资源可以有效节省带宽,并提高响应数据传输速度,配置后Nginx在响应报文发送至客户端之前进行压缩,然后再进行传输,浏览器收到之后再进行解压。
# 开启压缩 Syntax: gzip on | off; Default: gzip off; Context: http,server,location,if in location # 定义压缩类型,定义需要压缩的资源文件,资源类型可以在/etc/nginx/mime.types中查看或定义 Syntax: gzip_types mime-type ... ; Default: gzip_types text/html; Context: http,server,location # 定义压缩的级别(比率),一般3即可 # 压缩比率越高,传输效率越高,但是服务器压缩时、客户端解压时使用的CPU资源也会变高,所以适当即可 Syntax: gzip_comp_level [level number]; Default: gzip_comp_level 1; Context: http,server,location # 定义压缩协议版本,选择1.1版本即可,但因为默认就是1.1版本所以可不写 Syntax: gzip_http_version 1.0 | 1.1; Default: gzip_http_version 1.1; Context: http, server, location
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20配置方式
# 匹配需要压缩的资源进行压缩配置即可 location ~* \.(html|txt)$ { gzip on; gzip_types text/html text/plain; gzip_comp_level 3; }
1
2
3
4
5
6
# 防止资源盗链
防止其他网站恶意盗用自己网站的资源链接,从而减少不必要的带宽、系统等资源的浪费。
防盗链的方法有很多种,Nginx自带的防盗链功能主要是针对客户端请求时的请求头来进行验证,客户端在请求的过程中会携带的referer信息,referer会告诉服务器请求是从哪一个页面来的,然后Nginx可以根据referer信息进行防盗链。这种方式的优点是规则简单、配置方便,缺点是referer信息可以被伪造,非100%可靠,但它能限制大部分的盗链情况。
# 添加信任引用列表 # 如果来源域名不在该列表中,则$invalid_referer会返回1,我们可以对此进行判断进行处理 Syntax: valid_referers none | blocked | server_names | string ...; Default: — Context: server, location # none - 表示来源头部为空的情况 # blocked - 表示来源头部不为空的情况 # server_names - 来源头部包含的域名情况 (可正则匹配)
1
2
3
4
5
6
7
8
9配置方式
# 拒绝访问 location *~ \.(jpg|jpeg|gif|png)$ { # 允许referer中域名为*.test.com的请求进行访问,其他的则拒绝 valid_referers none blocked *.test.com; if ($invalid_referer) { return 403; } } # 替换图片,可以替换成一张有水印的图片 location *~ \.(jpg|jpeg|gif|png)$ { # 允许referer中域名为*.test.com的域名进行访问,其他的则拒绝 valid_referers none blocked *.test.com; if ($invalid_referer) { # break表示重写完后就停止匹配 rewrite ^(.*)$ /public/ks.jpg break; } } # 允许搜索引擎盗用 location *~ \.(jpg|jpeg|gif|png)$ { # 允许referer中的域名为*.test.com的请求和域名中包含.baidu的请求进行访问,其他的则拒绝 valid_referers none blocked *.test.com server_names ~\.baidu\.; if ($invalid_referer) { return 403; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27