WEB服务-Nginx之14-性能优化

WEB服务-Nginx之15-性能优化

概述

我们在做性能优化工作前,重点需要考虑和了解:

1、首先需要了解我们当前系统的结构和瓶颈,了解当前使用的是什么,运行的是什么业务,都有哪些服务,了解每个服务最大能支撑多少并发。

比如:nginx作为静态资源服务并发是多少,最高瓶颈在哪里,能支持多少qps(每秒查询率)的访问请求。

那我们怎么得出这组系统结构瓶颈呢?

比如:top查看系统的CPU负载、内存使用率、总得运行进程等,也可以通过日志去分析请求的情况,当然也可以通过我们前面介绍到的stub_statius模块查看当前的连接情况,也可以对线上的业务进行压力测试(低峰期),去了解当前这套系统能承担多少的请求和并发,做好响应的评估。

2、其次我们需要了解业务模式。虽然我们是做性能优化,但每一个性能的优化都是为业务所提供的服务的。

  • 我们需要了解每个业务接口的类型。

    比如:电商网站中的抢购模式,这种情况下面,平时没什么流量,但到了抢购时间流量会突增。

  • 我们还需要了解系统层次化的结构。

    比如:我们使用nginx做的是代理、还是动静分离、还是后端直接服务用户,那么这个就需要我们对每一层做好相应的梳理,以便更好的服务业务。

3、最后我们需要考虑性能与安全。往往注重了性能,但是忽略了安全。往往过于注重安全,对性能又会产生影响。

比如:我们在设计防火墙功能时,检测过于严密,这样就会给性能带来影响。那么如果对于性能完全追求,却不顾服务的安全,这个也会造成很大的隐患。

所以需要评估好两者的关系,把握好两者的孰重孰轻。以及整体的相关性,权衡好对应的点。


从OSI模型考虑优化方向

  • 硬件:代理(CPU) 静态(磁盘IO) 动态(cpu、内存)
  • 网络:带宽、丢包、延迟
  • 系统:文件描述符(文件句柄数)
  • 应用:代理(长连接 http1.1)
  • 服务:静态资源服务优化做缓存,做压缩,防盗链,跨域访问

影响性能指标

  1. 网络
    (1)网络的流量
    (2)网络是否丢包
    (3)这些会影响http的请求与调用
  2. 系统
    (1)硬件有没有磁盘损坏,磁盘速率
    (2)系统的负载、内存、系统稳定性
  3. 服务
    (1)连接优化。请求优化
    (2)根据业务形态做对应的服务设置
  4. 程序
    (1)接口性能
    (2)处理速度
    (3)程序执行效率
  5. 数据库

每个服务与服务之间都或多或少有一些关联,我们需要将整个架构进行分层,找到对应系统或服务的短板,然后进行优化


压力测试工具

​ 在系统业务量没有增长之前,我们就要做好相应的准备工作,以防患业务量突增带来的接口压力,所以对于接口压力测试就显得非常重要了,我们首先要评估好系统压力,然后使用工具检测当前系统情况,是否能满足对应压力的需求。


安装压力测试工具ab

[root@web01 ~]# yum -y install httpd-tools

ab

ab命令是Apache的Web服务器的性能测试工具,它可以测试安装Web服务器每秒种处理的HTTP请求。

ab [options] [http[s]://]hostname[:port]/path
   -A	指定连接服务器的基本的认证凭据
   -c	指定一次向服务器发出请求数,并发数
   -C	添加cookie
   -g	将测试结果输出为“gnuolot”文件
   -H	为请求追加一个额外的头
   -k   使用长连接
   -i	使用“head”请求方式
   -n   指定要执行的请求数
   -p	指定包含数据的文件
   -q	不显示进度百分比
   -T	使用POST数据时,设置内容类型头
   -v   设置详细模式等级
   -w   以HTML表格方式打印结果
   -x	以表格方式输出时,设置表格的属性
   -X   使用指定的代理服务器发送请求
   -y	以表格方式输出时,设置表格属性

示例

# 压测
ab -n 200 -c 2 127.0.0.1

# post传参压测
ab -n 400 -c 20 -p parm.txt -T "application/x-www-form-urlencoded" 127.0.0.1

web01提供nginx静态网站配置,创建所需页面,重载服务并访问测试

[root@web01 ~]# cat > /etc/nginx/conf.d/ab.conf << EOF
server {
    listen 80;

    location / {
        root /code;
        index index.html;
    }
}
EOF

[root@web01 ~]# echo "nginx ab" > /code/ab.html
[root@web01 ~]# systemctl reload nginx
[root@web01 ~]# curl 127.0.0.1/ab.html
nginx ab
[root@web01 ~]# ab -n 10000 -c 200 127.0.0.1/ab.html
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        nginx/1.18.0
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /ab.html
Document Length:        9 bytes

Concurrency Level:      200
Time taken for tests:   0.841 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2380000 bytes
HTML transferred:       90000 bytes
Requests per second:    11884.28 [#/sec] (mean)
Time per request:       16.829 [ms] (mean)
Time per request:       0.084 [ms] (mean, across all concurrent requests)
Transfer rate:          2762.17 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    2   0.7      2       5
Processing:     3    8   5.8      8     212
Waiting:        0    6   5.7      6     208
Total:          5   11   5.7     10     213

Percentage of the requests served within a certain time (ms)
  50%     10
  66%     11
  75%     11
  80%     11
  90%     12
  95%     12
  98%     14
  99%     15
 100%    213 (longest request)

web02提供tomcat静态网站配置,创建所需页面,启动服务并访问测试

[root@web02 ~]# yum -y install tomcat
[root@web02 ~]# echo "oldboy_tomcat" > /usr/share/tomcat/webapps/ROOT/tomcat.html
[root@web02 ~]# systemctl start tomcat
[root@web02 ~]# curl 127.0.0.1:8080/tomcat.html
oldboy_tomcat
[root@web02 ~]# ab -n 10000 -c 200 127.0.0.1:8080/tomcat.html
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        Apache-Coyote/1.1
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /tomcat.html
Document Length:        14 bytes

Concurrency Level:      200
Time taken for tests:   5.021 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      2570000 bytes
HTML transferred:       140000 bytes
Requests per second:    1991.60 [#/sec] (mean)
Time per request:       100.422 [ms] (mean)
Time per request:       0.502 [ms] (mean, across all concurrent requests)
Transfer rate:          499.85 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   36 184.1      2    1228
Processing:     1   54  82.5     24    1038
Waiting:        1   49  75.9     22     911
Total:          2   90 212.1     26    1415

Percentage of the requests served within a certain time (ms)
  50%     26
  66%     36
  75%     54
  80%     67
  90%    217
  95%    414
  98%   1065
  99%   1281
 100%   1415 (longest request)

很明显nginx在处理静态资源时要比tomcat快的多。


系统性能优化

增大打开最大文件数

​ 文件句柄,Linux一切皆文件,文件句柄可以理解为就是一个索引,文件句柄会随着我们进程的调用频繁增加,系统默认文件句柄是有限制的,不能让一个进程无限的调用,所以我们需要限制每个 进程和每个服务使用多大的文件句柄,文件句柄也是必须要调整的优化参数。


永久修改,修改配置文件/etc/security/limits.conf

  1. 系统全局性修改

    * - nofile 65535
    
  2. 用户局部性修改(常用)

    root - nofile 65535
    

[root@web01 ~]# sed -n '16,48p' /etc/security/limits.conf
#<domain>        <type>  <item>  <value>
#
#Where:
#<domain> can be:
#        - 一个用户名
#        - 一个用户组名, 语法 @group
#        - *, 表示所有用户
#        - %, 或者 %group, 最大登录限制
#
#<type> can have the two values:
#        - "soft" 执行软限制,超过仅提醒
#        - "hard" 执行硬限制,不能超过
#        - "-" 执行软限制和硬限制相同
#
#<item> can be one of the following:
#        - core - core文件的最大值 (KB)
#        - data - 进程的数据段最大值 (KB)
#        - fsize - 最大文件大小 (KB)
#        - memlock - 一个任务锁住的物理内存的最大值 (KB)
#        - nofile - 打开最大文件数
#        - rss - 最大驻留集大小 (KB)
#        - stack - 进程的栈的最大值 (KB)
#        - cpu - 指定CPU使用时间的上限 (MIN)
#        - nproc - 当前用户同时打开的进程(包括线程)
#        - as - 地址空间限制 (KB)
#        - maxlogins - 用户日志最大数量
#        - maxsyslogins - 系统日志最大数量
#        - priority - 运行用户进程的优先级
#        - locks - 用户可以持有的最大文件锁数
#        - sigpending - 挂起信号的最大数量
#        - msgqueue - POSIX消息队列使用的最大内存 (bytes)
#        - nice - 允许提升到值的最大nice优先级: [-20, 19]
#        - rtprio - 最大实时优先级

ulimit

ulimit 用于限制 shell 启动进程所占用的资源,支持以下各种类型的限制:所创建的内核文件的大小、进程数据块的大小、Shell 进程创建文件的大小、内存锁住的大小、常驻内存集的大小、打开文件描述符的数量、分配堆栈的最大大小、CPU 时间、单个用户的最大线程数、Shell 进程所能使用的最大虚拟内存。它支持硬资源和软资源的限制。

作为临时限制,ulimit 可以作用于通过使用其命令登录的 shell 会话,在会话终止时便结束限制,并不影响其他 shell 会话。而对于长期的固定限制,ulimit 命令语句又可以被添加到由登录 shell 读取的文件中,作用于特定的 shell 用户。

ulimit [-SHacdefilmnpqrstuvx] [limit]
       -a                显示目前资源限制的设定
       -c <core文件上限>  设定core文件的最大值,单位为区块
       -d <数据节区大小>   程序数据节区的最大值,单位为KB
       -f <文件大小>      shell所能建立的最大文件,单位为区块
       -H                设定资源的硬限制
       -m <内存大小>      指定可使用内存的上限,单位为KB
       -n <文件数目>      指定同一时间最多可开启的文件数
       -p <缓冲区大小>    指定管道缓冲区的大小,单位512字节
       -s <堆叠大小>      指定堆叠的上限,单位为KB
       -S                设定资源的软限制
       -t <CPU时间>       指定CPU使用时间的上限,单位为秒
       -u <程序数目>      用户最多可开启的程序数目
       -v <虚拟内存大小>   指定可使用的虚拟内存上限,单位为KB
[root@web01 ~]# ulimit -a
core file size          (blocks, -c) 0          # core文件的最大值为100 blocks。
data seg size           (kbytes, -d) unlimited  # 进程的数据段可以任意大。
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited  # 文件可以任意大。
pending signals                 (-i) 7828       # 最多有7828个待处理的信号。
max locked memory       (kbytes, -l) 64         # 一个任务锁住的物理内存的最大值为64KB。
max memory size         (kbytes, -m) unlimited  # 一个任务的常驻物理内存的最大值。
open files                      (-n) 65535      # 一个任务最多可以同时打开65535的文件。
pipe size            (512 bytes, -p) 8          # 管道的最大空间为4096字节。
POSIX message queues     (bytes, -q) 819200     # POSIX的消息队列的最大值为819200字节。
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192       # 进程的栈的最大值为8192字节。
cpu time               (seconds, -t) unlimited  # 进程使用的CPU时间。
max user processes              (-u) 65535      # 当前用户同时打开的进程(包括线程)65535。
virtual memory          (kbytes, -v) unlimited  # 没有限制进程的最大地址空间。
file locks                      (-x) unlimited  # 所能锁住的文件的最大个数没有限制。

端口重用

​ 在高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭TCP连接,socket变为TIME_WAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。

注意:

  1. 高并发会导致服务器在短时间范围内同时占用大量端口,而端口有个0~65535的范围,并不是很多,去除系统和其他服务要用的,剩下的就更少了。
  2. 在这个场景中,短连接表示业务处理+传输数据的时间 远远小于 TIME_WAIT的超时时间的连接。

比如获取一个web页面,1秒钟的http短连接处理完业务,在关闭连接之后,这个业务用过的端口会停留在TIME_WAIT状态几分钟,而这几分钟,其他HTTP请求是无法占用此端口的。单用这个业务计算服务器的利用率会发现,服务器处理业务的时间和端口(资源)被挂着无法被使用的时间的比例是 1:几百,服务器资源严重浪费。

这时就需要调整内核参数:让time_wait状态重用

[root@web01 ~]# cat >> /etc/sysctl.conf <<EOF
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1
EOF
[root@web01 ~]# sysctl -p    # 从配置文件中读取值
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1

# 查看指定内核参数的值
[root@web01 ~]# sysctl -n net.ipv4.tcp_tw_reuse net.ipv4.tcp_timestamps
# 查看所有内核参数
[root@web01 ~]# sysctl -a

长连接业务的服务就不需要考虑TIMEWAIT状态。在实际业务场景中,一般长连接对应的业务的并发量并不会很高。


sysctl.conf


1、每个网络接口的处理速率比内核处理包的速度快的时候,允许发送队列的最大数目。

net.core.netdev_max_backlog = 1000  # 默认1000,建议:102400

2、调节系统同时发起的TCP连接数,默认值一般为128,在客户端存在高并发请求的时候,128就变得比较小了,可能会导致链接超时或者重传问题。

net.core.somaxconn = 128  # 默认128,高并发的情况时候要设置大一些,建议:102400

3、设置系统中做多允许多少TCP套接字不被关联到任何一个用户文件句柄上,如果超出这个值,没有与用户文件句柄关联的TCP套接字将立即被复位,同时给出警告信息,这个值是简单防止DDOS(Denial of service)的攻击,在内存比较充足的时候可以设置大一些:

net.ipv4.tcp_max_orphans = 32768  # 默认32768,建议:102400

4、记录尚未收到客户度确认消息的连接请求的最大值,一般要设置大一些:

net.ipv4.tcp_max_syn_backlog = 256  # 默认256,建议:102400

5、设置时间戳,可以避免序列号的卷绕,有时候会出现数据包用之前的序列号的情况,此值默认为1表示不允许序列号的数据包,对于Nginx服务器来说,要改为0禁用对于TCP时间戳的支持,这样TCP协议会让内核接受这种数据包,从而避免网络异常,如下:

net.ipv4.tcp_timestamps = 1 # 默认1,建议:0

6、设置内核放弃TCP连接之前向客户端发生SYN+ACK包的数量,网络连接建立需要三次握手,客户端首先向服务器发生一个连接请求,服务器收到后由内核回复一个SYN+ACK的报文,这个值不能设置过多,会影响服务器的性能,还会引起syn攻击:

net.ipv4.tcp_synack_retries = 5 # 默认为5,避免syn攻击,建议:1

7、与上一个功能类似,设置为1即可:

net.ipv4.tcp_syn_retries = 5 # 默认为5,避免syn攻击,建议:1

代理服务优化

​ nginx作为代理服务,负责转发用户的请求,那么在转发的过程中建议开启HTTP长连接,用于减少握手次数,降低服务器损耗。


开启长连接


nginx代理服务配置

[root@lb01 ~]# vim /etc/nginx/conf.d/proxy.conf 
upstream web {
    server 172.16.1.7:80;
    keepalive 16;				# 开启长连接
}

server {
    listen 80;
    server_name linux.node.com;

    location / {
        proxy_pass http://web;
        include proxy_params;
    }
}
[root@lb01 ~]# cat /etc/nginx/proxy_params
proxy_http_version 1.1;         # 指定http协议为1.1
proxy_set_header Connection ""; # 清除“connection”头字段
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;  # 平滑过渡
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwared-For $proxy_add_x_forwarded_for;
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;           # 缓冲代理接收单个长连接内包含的web响应的数量和大小

fastcgi服务器配置

[root@web01 ~]# cat /etc/nginx/conf.d/wordpress.conf
server {
	listen 80;
	server_name linux.blog.com;
	root /code/wordpress;
	
	location ~* \.php$ {
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		fastcgi_keep_conn on;         # 开启长连接
		fastcgi_connect_timeout 60s;
		include fastcgi_params;
	}
}

ngx_http_upstream_module模块用于定义可以由proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass和 grpc_pass指令引用的服务器组。


Syntax:  keepalive connection;
Default: -
Context: upstream

指定长连接的最大数量。

connections参数设置每个work进程的缓存中保留的与上游服务器的空闲保持连接的最大数量。超过此数目时,将关闭最近最少使用的连接。

HTTP, 推荐设置proxy_http_version 1.1;proxy_set_header Connection "";

upstream http_backend {
    server 127.0.0.1:8080;

    keepalive 16;
}

server {
    ...

    location /http/ {
        proxy_pass http://http_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        ...
    }
}

注意:

  1. keepalive指令不限制work进程可以打开的与上游服务器的连接总数。connections参数应设置为足够小的数字,以使上游服务器可以处理新的传入连接。

  2. 使用FastCGI服务,必须设置fastcgi_keep_conn on;

  3. 必须在keepalive指令之前指定负载平衡调度算法。

  4. SCGI和uwsgi协议没有保持连接的概念。

  5. proxy、fastcgi、uwsgi协议都有cache功能,开启后可加速网站访问的效率(取决于硬件)。


Syntax: 	keepalive_requests number;
Default:	keepalive_requests 100;
Context:	upstream

keepalive_requests设置通过一个keepalive连接可以处理的最大请求数,在发出最大请求数后,将关闭连接。

要释放每个连接的内存分配,必须定期关闭连接。因此,使用过多的最大请求数可能会导致过多的内存使用。


Syntax: 	keepalive_timeout timeout;
Default:	keepalive_timeout 60s;
Context:	upstream

keepalive_timeout设置超时,在此超时期间与上游服务器的空闲keepalive连接将保持打开状态。


静态资源优化

Nginx作为静态资源Web服务器,用于静态资源处理,传输非常的高效

graph LR 客户 --REQ: jpeg html flv--> Nginx --> id2((静态文件存储))

静态资源:指非WEB服务器端运行处理而生成的文件

静态资源类型 种类
浏览器渲染 HTML、CSS、JS
图片文件 JPEG、GIF、PNG
视频文件 FLV、Mp4、AVI
其他文件 TXT、DOC、PDF、...

浏览器缓存

浏览器缓存设置用于提高网站性能,尤其是新闻网站,图片一旦发布,改动的可能是非常小的,所以我们希望能否用户访问一次后,图片缓存在用户的浏览器长时间缓存。 浏览器是有自己的缓存机制,基于HTTP协议缓存机制来实现,在HTTP协议中有很多头信息,实现浏览器缓存需要依赖特殊的头信息来与服务器进行特殊的验证,如Expires(http/1.0);Cache-control(http/1.1)。

graph LR 浏览器无缓存 客户 --> id1((无缓存)) --> id3((请求web服务器)) --> id4((请求后端服务器处理)) id4((请求后端服务器处理)) --> id5((web服务器)) --> 浏览器
graph LR 浏览器无缓存 客户 --> id1((有缓存)) --> id3((校验过期)) --> 浏览器

浏览器缓存过期校验机制

st=>start: 浏览器请求
e=>end: 给浏览器

op0=>operation: 有缓存
op1=>operation: 向web服务器请求If-None-Match
op2=>operation: 向web服务器请求If-Modified-Since
op3=>operation: 从缓存读取
op4=>operation: 向web服务器请求
op5=>operation: 请求后端,缓存协商
op6=>operation: 服务器决策
op7=>operation: 资源
op8=>operation: 返回给

cond1=>condition: 是否过期?
cond2=>condition: 有无Etag
cond3=>condition: last-Modified
cond4=>condition: 200(yes) or 304(no)

st->op0->cond1
cond1(yes)->cond2
cond1(no,bottom)->op3
op3->op7->op8->e
cond2(yes)->op1->op6
op6->cond4
cond2(no,bottom)->cond3
cond3(yes)->op2(right)->op6->cond4
cond3(no,bottom)->op4->op5(bottom)
op5(bottom)->e
cond4(yes)->op5(bottom)->e
cond4(no)->e

说明:

1.浏览器会先去查看响应头部的cache-control(缓存控制)
2.如果没有到达过期时间,会直接返回缓存中的内容,不需要重新读取服务器
3.如果cache-control设置为 no-cache,浏览器会去读取expires(缓存过期时间)
4.如果没有到达expires过期时间,会直接返回缓存中的内容,不需要重新读取服务器
5.如果cache-control和expires都没有设置或者缓存过期,浏览器会去查看服务器上面有无ETag值
6.如果存在那么客户端会向web服务器请求If-None-Match,与Etag值进行比对,由服务器决策返回200还是304。
7.如果ETag与浏览器的 If-None-Match 相同,则走缓存
8.如果ETag不存在或与浏览器的 If-None-Match 不相同,浏览器会去查看服务器上面的Last-Modified值
9.如果服务器上有 Last-Modified值,浏览器会拿着If-Modified-Since去跟他对比
10.如果Last-Modified值与浏览器的 If-Modified-Since 相同,则走缓存
11.如果Last-Modified值与浏览器的 If-Modified-Since 不相同,重新去服务器读取数据

Last-Modified:服务器上文件的最后修改时间
Etag:文件唯一标识符
Expires:本地缓存目录中,文件过期的时间(由服务器指定具体的时间)
Cache-control:本地缓存目录中,文件过期的时间(由服务器指定过期的间隔时间,由于浏览器根据间隔生成具体的时间)
If-None-Match:浏览器上保留的文件唯一标识符
If-Modified-Since:浏览器上保留的文件最后修改时间

浏览器缓存

# 响应头部
cache-control: max-age=2592000
expires: Thu, 19 Nov 1981 08:52:00 GMT
ETag: "5f3fefc8-2d03"
Last-Modified: Fri, 21 Aug 2020 16:01:12 GMT

# 请求头部
If-Modified-Since: Fri, 21 Aug 2020 16:01:12 GMT
If-None-Match: "5f3fefc8-2d03"

Syntax:  expires [modified] time;
         epoch | max | off;
Default: expires off;
Context: http, server, location, if in location

expires :添加Cache-Control Expires头

Nginx expires的功能就是为用户访问的网站内容设定一个过期时间,当用户第一次访问这些内容时,会把这些内容存储在用户浏览器本地,这样用户第二次及以后继续访问该网站时,浏览器会检查加载已经缓存在用户浏览器本地的内容,就不会去服务器下载了,直到缓存的内容过期或被清除为止。

Nginx expires功能优点:

1.expires可以降低网站的带宽,节约成本。

2.加快用户访问网站的速度,提升用户访问体验。

3.服务器访问量降低了,服务器压力就减轻了,服务器成本也会降低,甚至可以节约人力成本。

4.对于几乎所有的Web服务来说,这是非常重要的功能之一,Apache服务也有此功能。


浏览器缓存静态资源

server {
    listen 80;
    server_name static.oldboy.com;

    location ~ .*\.(jpg|gif|png)$ {
        expires 7d;
    }
    location ~ .*\.(js|css)$ {
        expires 30d;
    }
}

浏览器不缓存

企业网站有可能不希望被缓存的内容:

  1. 广告图片,用于广告服务,都缓存了就不好控制展示了。

  2. 网站流量统计工具(JS代码),都缓存了流量统计就不准了。

  3. 更新很频繁的文件(google的logo),这个如果按天,缓存效果还是显著的。

  4. 如果代码没有正式上线,测试化境经常更新前端代码,需要关闭缓存


关闭缓存的方式:

  1. 使用无痕模式
  2. 开启浏览器 Disable cache
  3. 配置nginx,在响应头中添加不缓存
location ~ .*\.(js|css|html)$ {
    add_header Cache-Control no-store;
    add_header Pragma no-cache;
}

读取方式

1)文件高速读取

Syntax: 	sendfile on | off;
Default:	sendfile off;
Context:	http, server, location, if in location

2)高效传输(必须开启sendfile)

将多个包一次发送,用于提升网络传输效率,大文件推荐打开

Syntax: 	tcp_nopush on | off;
Default:	tcp_nopush off;
Context:	http, server, location

3)长连接传输(必须开启长连接)

提高网络传输实时性,需要开启keepalive,来一个包发一个包不等待,小文件推荐打开

Syntax: 	tcp_nodelay on | off;
Default:	tcp_nodelay on;
Context:	http, server, location

注意:tcp_nodelaytcp_nopush同时只能配置一个,不能同时配置


传输压缩

Nginx将响应报文发送至客户端之前启用压缩功能,然后进行传输,传输后解压,这能够有效地节约带宽,并提高响应至客户端的速度。


开启压缩

Syntax:  gzip on | off;
Default: gzip off;
Context: http, server, location, if in location

指定压缩类型

Syntax:  gzip_types mime-type ...;
Default: gzip_types text/html;
Context: http, server, location

指定压缩比例(1-9个级别)

Syntax:  gzip_comp_level level;
Default: gzip_comp_level level 1;
Context: http, server, location

指定传输协议

Syntax:  gzip_http_version 1.0 | 1.1;
Default: gzip_http_version 1.1;
Context: http, server, location

示例:

[root@lb01 conf.d]# cat try.conf 
server {
    listen 80;
    server_name try.oldboy.com;

    location ~ .*\.(jpg|png|gif) {
        root /code/images;
        #gzip on;
        #gzip_comp_level 2;
        #gzip_http_version 1.1; 
        #gzip_types image/jpeg image/gif image/png;
    }

    location ~ .*\.(txt|xml|html|json|js|css)$ {
        gzip on;
        gzip_comp_level 1;
        gzip_http_version 1.1;
        gzip_types text/plain application/json application/x-javascript application/css application/xml text/javascript;
    }
}

gzip对于图片压缩的比率不太明显,545K可以压缩到539K。

gzip对于文件压缩的比率很高,655K可以压缩到153K。


ngx_http_gzip_module

#启用或禁用gzip压缩,默认关闭
gzip on | off;
 
#压缩比由低到高从1到9,默认为1
gzip_comp_level level;
 
#禁用IE6 gzip功能
gzip_disable "MSIE [1-6]\.";
 
#gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
 
#启用压缩功能时,协议的最小版本,默认HTTP/1.1
gzip_http_version 1.0 | 1.1;
 
#指定Nginx服务需要向服务器申请的缓存空间的个数*大小,默认32 4k|16 8k;
gzip_buffers number size;
 
#指明仅对哪些类型的资源执行压缩操作;默认为gzip_types text/html,不用显示指定,否则出错
gzip_types mime-type ...;
 
#如果启用压缩,是否在响应报文首部插入“Vary: Accept-Encoding”
gzip_vary on | off;
 
#nginx充当代理服务器时,对于后端服务器的响应报文,在何种条件下启用压缩功能
gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any ...;
 
#off:不启用压缩
#expired,no-cache, no-store,private:对后端服务器的响应报文首部Cache-Control值任何一个,启用压缩功能

防止资源盗链

如果别人只链接了自己网站图片或某个单独的资源,而不是打开了网站的整个页面,这就是盗链。

防盗链,指的是防止资源被其他网站恶意盗用。

基础防盗链设置思路:主要是针对客户端请求过程中所携带的一些Header信息来验证请求的合法性,比如客户端在请求的过程中都会携带referer信息。优点是规则简单,配置和使用都很方便,缺点是防盗链所依赖的Referer验证信息是可以伪造的,所以通过referer信息防盗链并非100%可靠,但能够限制大部分的盗链情况。

referer记录打开一个页面之前从哪个页面跳转过来。


ngx_http_referer_module模块用于阻止“ Referer”标头字段中有无效值的请求访问站点。

Syntax: 	valid_referers none | blocked | server_names | string ...;
Default:	-
Context:	server, location

指定有效的“ Referer”标头字段。

指定参数将嵌入式变量$invalid_referer设置为空字符串,允许访问。否则,将变量设置为“ 1”,阻止访问。

搜索匹配项不区分大小写。

参数如下:

  • none:请求标头中缺少“ Referer”字段;
  • blocked:请求标头中存在“ Referer”字段,但其值已被防火墙或代理服务器删除,或是不以“ http://”或“ https://” 开头的字符串;
  • server_names:“ Referer”请求标头字段包含服务器名称之一;
  • arbitrary string:定义服务器名称和可选的URI前缀。服务器名称的开头或结尾可以带有*。在检查过程中,“ Referer”字段中的服务器端口将被忽略;
  • regular expression:第一个符号应为“ ~”。注意,表达式与“ http://”或“ https://” 之后的文本匹配。

例:

valid_referers none blocked server_names
               *.example.com example.* www.example.org/galleries/
               ~\.google\.;

盗链

  1. 配置盗链服务器web01和被盗链服务器web02并重载
[root@web01 ~]# cat > /etc/nginx/conf.d/daolian.conf <<EOF
server {
    listen 80;
    server_name www.daolian.com;

    location / {
        root /code;
        index daolian.html;
    }
}
EOF

[root@web02 ~]# cat > /etc/nginx/conf.d/beidaolian.conf <<EOF
server {
    listen 80;
    server_name www.beidaolian.com;

    location / {
        root /code;
        index daolian.html;
    }
}
EOF

[root@web01 ~]# systemctl reload nginx
[root@web02 ~]# systemctl reload nginx
  1. 创建被盗链接:
[root@web02 ~]# wget -O /code/daolian.png area.sinaapp.com/bingImg/
  1. 创建盗链页面:
[root@web01 ~]# cat > /code/daolian.html <<EOF
<html>
<head>
    <meta charset="utf-8">
    <title>oldboy</title>
</head>
<body style="background-color:pink;">
    <center><img src="http://www.beidaolian.com/daolian.png"/></center>
</body>
</html>
EOF
  1. windows配置:

C:\Windows\System32\drivers\etc\hosts文件中添加一行10.0.0.7 www.daolian.com和一行10.0.0.8 www.beidaolian.com

  1. 访问页面http://www.beidaolian.com/daolian.png和http://www.daolian.com测试

image-20200925204433814

image-20200925204456658


防盗链

  1. 只有所有来自~.google. ~.baidu.的请求可以访问到当前站点的图片,如果来源域名不在这个列表中,那么$invalid_referer等于1,if返回403错误,也就是配置白名单,指定希望可以盗链的网站
# 指定有效referer,除了指定网址可以调用,其他网址都不可以调用
valid_referers none block server_names ~\.google\. ~\.baidu\.;
# 除有效referer外,其他网址调用就会返回状态:错误403
if ($invalid_referer) {
    return 403 "Forbidden Access";
}
  1. 添加防盗链,重载服务测试http://www.daolian.com

image-20200925205406378


  1. 还可以使用rewrite返回一个URL,可以是图片,广告,或者主页...
[root@web02 ~]# cat > /etc/nginx/conf.d/beidaolian.conf <<EOF
server {
    listen 80;
    server_name www.beidaolian.com;

    location / {
        valid_referers none block server_names ~\.google\. ~\.baidu\.;
        if ($invalid_referer) {
            rewrite ^(.*)$ https://www.baidu.com break;
        }
        root /code;
        index daolian.html;
    }
}
EOF

  1. 当然这种防护并不能百分百保证资源不被盗链,因为我们可以通过命令来修改来源的refer信息,伪造协议头访问
[root@web01 ~]# echo 10.0.0.8 www.beidaolian.com >> /etc/hosts
[root@web01 ~]# curl -e "https://www.oldboy.com" -I http://www.beidaolian.com/daolian.png
HTTP/1.1 413 Request Entity Too Large
Server: nginx/1.18.0
Date: Fri, 25 Sep 2020 13:02:03 GMT
Content-Type: image/png
Content-Length: 16
Connection: keep-alive

[root@web01 ~]# curl -e "https://www.baidu.com" -I http://www.beidaolian.com/daolian.png
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Fri, 25 Sep 2020 13:02:16 GMT
Content-Type: image/png
Content-Length: 341385
Last-Modified: Fri, 25 Sep 2020 12:09:49 GMT
Connection: keep-alive
ETag: "5f6dde0d-53589"
Accept-Ranges: bytes

允许跨域访问

跨域访问

当我们通过浏览器访问a网站时,利用到ajax或其他方式,同时也请求b网站,这样请求一个页面,使用了两个域名,浏览器默认禁止。

盗链就是由我的网站向你的网站发起get获取某个资源的请求

跨域访问就是由我的网站向你的网站发起ajax或其他方式访问网站的请求

浏览器会读取头信息Access-Control-Allow-Origin,如果服务端允许,浏览器不会拦截。


Syntax: add_header name value [always];
Default: -;
Context: http, server, location, if in location

web01配置跨域访问及所需文件

[root@web01 ~]# cat > /etc/nginx/conf.d/k.conf << EOF
server {
    listen 80;
    server_name k.oldboy.com;

    location / {
        root /code;
        index index.html;
    }
}
EOF

[root@web01 ~]# vi /code/k.html
<html lang="en">
<head>
        <meta charset="UTF-8" />
        <title>测试ajax和跨域访问</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://s.oldboy.com",
        success: function(data) {
                alert("sucess 成功了!!!");
        },
        error: function() {
                alert("fail!!,跨不过去啊,不让进去啊,只能...!");
        }
        });
});
</script>
        <body>
                <h1>跨域访问测试</h1>
        </body>
</html>

web02配置正常访问及所需文件

[root@web02 ~]# cat > /etc/nginx/conf.d/s.conf <<EOF
server {
    listen 80;
    server_name s.oldboy.com;
    root /code;
    index index.html;
    charset utf-8;
}
EOF
[root@web02 ~]# echo web02 > /code/index.html

windows配置:在C:\Windows\System32\drivers\etc\hosts文件中添加一行10.0.0.7 k.oldboy.com 和一行10.0.0.8 s.oldboy.com

重载服务,在浏览器访问测试http://s.oldboy.com和http://k.oldboy.com/k.html

[root@web01 ~]# systemctl reload nginx
[root@web02 ~]# systemctl reload nginx

image-20200925212753493

image-20200925211715347


web02配置允许跨域访问

[root@web02 ~]# vi /etc/nginx/conf.d/s.conf
server {
    listen 80;
    server_name s.oldboy.com;
    root /code;
    index index.html;
    charset utf-8;

    location ~ .*\.(html|htm)$ {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
    }
}

重载服务,再次浏览器访问测试http://k.oldboy.com/k.html

[root@web01 ~]# systemctl reload nginx

image-20200925213142743

CPU亲和与Worker进程

CPU亲和(affinity)减少进程之间不断频繁切换,减少性能损耗,其实现原理是建CPU核心和Nginx工作进程绑定方式,把每个worker进程固定到对应的cpu上执行,减少切换CPU的cache miss,获得更好的性能。

img


  1. 查看当前有多少个CPU核心,多少个work进程
[root@web01 ~]# lscpu | grep '^CPU(s):'
CPU(s):                4                   # 逻辑cpu颗数4个
[root@web01 ~]# pstree -p |grep nginx      # work进程4个
           |-nginx(1790)-+-nginx(1791)
           |             |-nginx(1792)
           |             |-nginx(1793)
           |             `-nginx(1794)

  1. 将nginx worker进程绑定至不同的核心上

1)方式一(不推荐)

worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;  # 将Nginx工作进程绑定到指定的CPU核心(当前代表的是4个CPU,4个work的意思)

2)方式二(不推荐)

worker_processes  2; 
#worker_cpu_affinity 01 10;
#worker_cpu_affinity 0101 1010;
worker_cpu_affinity 010101 101010;

3)方式三(推荐)

worker_processes auto; 
worker_cpu_affinity auto;

  1. 查看绑定结果
[root@web01 ~]# ps axo pid,psr,cmd |grep nginx|grep -v grep
  1790   2 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
  1791   0 nginx: worker process
  1792   1 nginx: worker process
  1793   2 nginx: worker process
  1794   3 nginx: worker process

Core functionality核心功能


worker_processes定义工作进程数(建议与CPU数量一致或auto)。

Syntax:	  worker_processes number | auto;
Default:  worker_processes 1;
Context:  main

worker_cpu_affinity将Nginx工作进程绑定到指定的CPU核心。每个CPU集由允许的CPU的位掩码表示。应该为每个工作进程定义一个单独的集合。默认情况下,辅助进程未绑定到任何特定的CPU。

绑定并不是意味着当前nginx进程独占用一核心CPU,但是可以保证此进程不会运行在其他核心上,这就极大减少了nginx的工作进程在不同的cpu核心上的来回跳转,减少了CPU对进程的资源分配与回收以及内存管理等,因此可以有效的提升nginx服务器的性能。简单来说就是提高缓存命中率。

Syntax:	  worker_cpu_affinity cpumask ...;
          worker_cpu_affinity auto [cpumask];
Default:  -
Context:  main

worker_priority定义工作进程的调度优先级,就像通过nice命令一样:负数 number 表示更高的优先级。允许范围通常为-20至20。

Syntax:	  worker_priority number;
Default:  worker_priority 0;
Context:  main

worker_rlimit_nofile设置RLIMIT_NOFILE工作进程最大打开文件数。用于在不重新启动主进程的情况下增加限制。number包括Nginx的所有连接(例如与代理服务器的连接等),而不仅仅是与客户端的连接,另一个考虑因素是实际的并发连接数不能超过系统级别的最大打开文件数的限制。

Syntax:	  worker_rlimit_nofile number;
Default:  -
Context:  main

示例:

worker_rlimit_nofile 65535; # 所有worker进程能打开的文件数量上限,最好与 ulimit -n 的值保持一致,如65535

禁止IP直接访问

当用户通过访问IP或者未知域名访问你得网站的时候,你希望禁止显示任何有效内容,可以给他返回500,目前国内很多机房都要求网站关闭空主机头,防止未备案的域名指向过来造成麻烦


禁止IP访问直接返回错误

[root@web01 ~]# cat > /etc/nginx/conf.d/a_ip.conf << EOF
server {
    listen 80 default_server;
    server_name localhost;
    return 500;
}
EOF

禁止IP访问引流的方式跳转到主页

[root@web01 ~]# cat > /etc/nginx/conf.d/a_ip.conf << EOF
server {
    listen 80 default_server;
    server_name localhost;
    rewrite (.*) http://www.baidu.com;
    #return 302 http://www.baidu.com;
}
EOF

禁止IP访问返回指定内容

[root@web01 ~]# cat > /etc/nginx/conf.d/a_ip.conf << EOF
server {
    listen 80 default_server;
    server_name localhost;
    default_type text/plain;
    return 200 "请请求其他页面...或使用域名请求!";
}
EOF

禁止非法域名访问

方式一:配置一个在所有server虚拟主机区块最前面的区块,对于所有域名匹配不成功的请求返回501

[root@web01 ~]# cat > /etc/nginx/conf.d/aaaf.conf << EOF
server {
    listen 80;
    server_name _;
    return 501;
}
EOF

方式二:通过你的域名访问时候,自动跳转到我的域名上

[root@web01 ~]# cat > /etc/nginx/conf.d/aaaf.conf << EOF
server {
    listen 80 default_server;
    server_name _;
    
    if ($host !~ ^www\.nmtui\.com$) {
        rewrite ^(.*) http://k.oldboy.com/$1 permanent;
    }
}
EOF

调整上传文件大小限制

在使用nginx上传文件时,通常需要调整上传文件大小限制,避免出现413 Request Entity Too Large


Syntax:  client_max_body_size size;
Default: client_max_body_size 1m;
Context: http, server, location

示例

server {
    listen 80;
    server_name _;
    client_max_body_size 200m;
}

自定义error_page错误页面

配置跳转网络地址

#error_page配置的是http这种的网络地址
[root@lb01 conf.d]# cat error.conf 
server {
    listen       80;
    server_name  www.old.com;
    root /code;
    #error_page  404  http://www.baidu.com;

    location / {
        index index.html;
        error_page  404  http://www.baidu.com;
    }    
}

配置跳转本地地址

[root@lb01 conf.d]# cat error.conf 
server {
    listen 80;
    server_name error.old.com;
    root /code;

    location / {
        index index.html;
    }

    #error_page  403 404  /404.jpg;

    error_page 403 404 /404.html;
    location = /404.html {
        root /code;
        index index.html;   
    }
}

访问PHP的错误页面跳转

[root@web01 ~]# vim /etc/nginx/conf.d/error.conf
server {
    listen 80;
    server_name  linux.error.com;
    root /code/error;
    index index.php;
    error_page 404 403 /404.html;

    location ~* \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        if (!-e $request_filename) {
            rewrite (.*) http://linux.error.com/404.jpg;
        }
    }
}

响应标题字段版本隐藏优化

ngx_http_core_module模块


server_tokens在错误页面和“服务器”响应标题字段中启用或禁用Nginx版本显示。

Syntax:	    server_tokens on | off | build | string;
Default:	server_tokens on;
Context:	http, server, location

示例:

server_tokens off; # 禁用Nginx版本显示
# 启用时
[root@web01 ~]# curl 127.0.0.1
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.16.1</center>
</body>
</html>

# 禁用时
[root@web01 ~]# curl 127.0.0.1
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>

自定义响应报文的nginx版本信息,需要修改源码文件,重新编译安装。

  • server_tokens on;

    [root@web01 ~]# sed -n '13,14p' /opt/nginx-1.18.0/src/core/nginx.h 
    #define NGINX_VERSION      "1.18.0"
    #define NGINX_VER          "nginx/" NGINX_VERSION
    
  • server_tokens off;

    [root@web01 ~]# sed -n '49p' /opt/nginx-1.18.0/src/http/ngx_http_header_filter_module.c 
    static u_char ngx_http_server_string[] = "Server: nginx" CRLF;
    # 把"Server: nginx"中的nginx改为自己想要的文字即可
    

防爬虫

方式一:阻止下载协议代理,命令如下:

if ($http_user_agent ~* LWP::Simple|BBBike|wget) {
    return 403;
}

方式二:添加内容,防止N多爬虫代理访问网站,命令如下:

if ($http_user_agent ~* "qihoobot|Baiduspider|Googlebot|Googlebot-Mobile|Googlebot-Image|Mediapartners-Google|Adsbot-Google|Yahoo!Slurp China|YoudaoBot|Sosospider|Sogou spider|Sogou web spider|MSNBot") {
    return 403;
}

设置超时参数

  1. 设置参数: keepalive_timeout 60; # 长连接才有意义
syntax: keepalive_timeout timeout [header_timeout];
default:keepalive_timeout 75s;
context:http,server,location

# 说明:客户端和服务端都没有数据传输时,进行超时时间倒计时,一旦超时时间读取完毕还没有数据传输,就断开连接
  1. 设置参数:client_header_timeout 55;
syntax: client_header_timeout time;
default:client_header_timeout 60s;
context:http,server

# 说明:表示定义客户端请求报文发送的间隔超时时间,客户端发送的请求报文中请求头信息的间隔时间
  1. 设置参数:client_body_timeout 55;
syntax: client_body_timeout time;
default:client_body_timeout 60s;
context:http,server,location

# 说明:表示定义服务端响应报文发送的间隔超时时间,客户端发送的请求报文中请求主体信息的间隔时间
  1. 设置参数:send_timeout 60s;
syntax: send_timeout time;
default:send_timeout 60s;
context:http,server,location

# 说明:表示定义客户端读取服务端响应报文的间隔超时时间,服务端发送的响应报文间隔时间

Nginx通用优化配置

[root@web01 ~]# cat /etc/nginx/nginx.conf
# nginx进程启动用户
user www;
# 工作进程数 根据cup核心数自动设置数量
worker_processes auto;
# 将工作进程自动绑定到可用的cpu上
worker_cpu_affinity auto;

# 错误日志保存路径 级别警告
error_log /var/log/nginx/error.log warn;
# nginx启动后pid文件保存路径
pid /run/nginx.pid;
# 单个工作进程可以打开的文件句柄数,调整至1w以上,负荷较高建议2-3w
worker_rlimit_nofile 35535;
worker_priority -20;

# 事件驱动模块
events {
    # 使用epoll网络I/O模型
    use epoll;
    # 限制每个进程能处理多少个连接,10240x[cpu核心]
    worker_connections 10240;
    accept_mutex on;
    multi_accept on;
}

http {
    # 包含文件扩展名与文件类型的映射表
    include             mime.types;
    # 如果不识别,默认下载
    default_type        application/octet-stream;
    # 设置字符集为utf-8
    charset 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"';

#   $remote_addr :  		 # 上一层访问来的客服端IP地址
#   $remote_user : 			# 登陆的用户
#   [$time_local] :			# 本地访问时间
#   $request :				 # 请求类型
#   $status :       		 # 请求状态
#   $body_bytes_sent		 # 请求的数据字节大小
#   $http_referer            # 上次跳转过来的地址ip
#   $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;

    # 禁止浏览器显示nginx版本号
    server_tokens off;
    # 文件上传大小限制
    client_max_body_size 200m;

    # 文件高效传输,静态资源服务器建议打开
    sendfile            on;
    # 高效文件读取与高效文件传输搭配使用
    tcp_nopush          on;
    
    # 文件实时传输,动态资源服务建议打开,需要打开keepalive
    #tcp_nodelay         on;
    # 长连接请求结束后保持的时间
    #keepalive_timeout   65;

    # 开启压缩
    gzip on;
    # 禁用IE(1~6)版本浏览器的gzip压缩 
    gzip_disable "MSIE [1-6]\.";
    # 指定压缩后http使用的传输协议
    gzip_http_version 1.1;
    # 压缩级别
    gzip_comp_level 2;
    # 压缩缓冲区
    gzip_buffers 16 8k;
    # 允许压缩的页面最小字节数,默认值20
    gzip_min_length 1024;
     # 压缩的类型
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript image/jpeg;

    # 包含虚拟主机配置文件
    include /etc/nginx/conf.d/*.conf;
}

Nginx安全与优化总结

1、CPU亲和、worker进程数、调整每个worker进程打开的文件数

2、使用epool网络I/O模型、调整每个worker进程的最大连接数

3、文件的高效读取sendfile、tcp_nopush

4、文件的实时传输keepalive、tcp_nodelay

5、开启tcp长连接,以及长连接超时时间keepalive_timeout

6、开启文件传输压缩gzip

7、开启静态文件expires浏览器缓存

8、禁止浏览器显示nginx版本号

9、禁止通过ip地址访问,禁止恶意域名解析,只允许域名访问

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

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

12、自定义error_page错误页面

13、https加密传输

14、nginx proxy_cache、fastcgi_cache、uwsgi_cache 缓存,第三方工具(squid、varnish)

配置FastCGI优化

http {
    fastcgi_connect_timeout 240;
    fastcgi_send_timeout 240;
    fastcgi_read_timeout 240;
    fastcgi_buffer_size 64k;
    fastcgi_buffers 4 64k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 128k;
    fastcgi_cache_path /data/ngx_fcgi_cache levels=2:2 keys_zone=ngx_fcgi_cache:512m inactive=1d max_size=40g;

    server {
        fastcgi_cache ngx_fcgi_cache;
        fastcgi_cache_valid 200 302 1h;
        fastcgi_cache_valid 301 1d;
        fastcgi_cache_valid any 1m;
        fastcgi_cache_min_uses 1;
        fastcgi_cache_use_stale error timeout invalid_header http_500;
        fastcgi_cache_key http://$host$request_uri; 
    } 
}
Nginx FastCGI相关参数 说明
fastcgi connect timeout 表示nginx服务器和后端FastCGI服务器连接的超时时间,默认值为60秒,这个参数值通常不要超过75秒,因为建立的连接越多,消耗的资源就越多
fastcgi send timeout 设置nginx传输请求到FastCGI服务器的超时时间,这个超时时间不是整个请求的超时时间,而是两个成功请求的之间间隔时间为超时时间,如果这个时间内,FastCGI服务没有收到任何信息,连接将关闭
fastcgi read timeout 设置nginx从FastCGI服务器读取响应信息的超时时间苯示连捿建立成功后,nginx等待后端服务器的响应时间,是nginx进入后端的排队之中的等候处理的时间,实际上是读取FastCGI响应成功信息的间隔时间,
fastcgi buffer size 这是Nginx FastCGI的缓冲区大小参数,设定用来读取从FastCGI服务器端收到的第一部分响应信息的缓冲区大小,这里的第一部分通常会包含一个小的响应头部s默认情况下,这个参数的大小等价于_个内存页。不是4k就是8k 根据相应系统平台来决定,也可以更小。
fastcgi_buffers 设定用来读取从FastCGI服务器端收到的响应信息的缓冲区大小和缓冲区数是,默认值为fastcgi buffer 8 4k|8k;指定本地需要用多少和多大的缓冲区来缓冲FastCGI的应答请求,如果一个 PHP脚本产生的页面大小为256KB ,那么会为其分配4个64KB的缓冲区来缓存;如果页面大小大于256KB ,那么大于256KB的部分会缓存到fastcgi temp 指定的路径中,但是这并不是好方法,因为内存中的数据处理速度要快于硬盘。一般这个值应该为站点中PHP脚本产生的页面大小的中间值,如果站点大部分脚本所产生的页面大小为256KB ,那么可以把这个值设置为"16 16k" , "4 64k"等
fastcgi busy buffers_size 用于设置系统很忙时可以使用的fastcgi buffers大小,言方推荐的大小为fastcgi buffers 2 ;默认值为fastcgi busy buffers_size 8k|16k
fastcgi temp file write size FastCGI临时文件的大小,可以设置为128~256KB ; 默认fastcgitempfilewritesize 8k|16k;
fastcgi cache oldboy nginx 表示开后FastCGI缓存并为其指定一个名称。开后缓存非常有用,可以有效降低CPU的负载,并且防止502错误的发生,但是开后缓存也可能引起其它问题,要根据具体情况来选择
fastcgi cache path 实例:fastcgi cache path /data/nginx/cache levels = 2:2 keys zone = ngx fcgi cache:512m inactive = ld max size=40g; fastcgi cache缓存目录,可以设置目录前列层级,比如2:2会生成256*256 个子目录,keys zone是这个缓存空间的名字,cache是用多少内存(这样热门的内容,nginx会直接放入内存,提高访问速度)。inactive表示默认失效时间,max size表示最多用多少硬盘空间,雲要注意的是fastcgi cache缓存是先写在fastcgi temp path在移到fastcgi cache path中去的,所以这个两个目录最好在同一个分区,从0.8.9之后可以在不同的分区,不过还是建议放在同_分区。
fastcgi cache valid 示例:fastcgicachevalid 200 302 lh; 用来指定应答代码的缓存时间,实例中的值表示将200和302应答缓存1个小时; 示例:fastcgicachevalid 301 Id; 将301应答缓存1天;
fastcgi cache min_uses 示例:fastcgicachemin_uses 1; 设置清求几次之后晌应将被缓存,1表示一次即被缓存
**fastcgi cache use_stale ** 示例:fastcgicacheusestale error timeout invalidheader http_500 定义在哪些情况下使用过期缓存
**fastcgi cache key ** 示例:fastcgicachekey requestmethod://requestmethod://hostrequesturi;fastcgi.cache.keyhttp://requesturi;fastcgi.cache.keyhttp://host$requesturi;定义fastcgicache的key ,示例中以请求的URI作为缓存的key,nginx会取这个key的md5作为缓存文件,如果设置了缓存散列目录,nginx会从后往前取梠应的位数作为目录。注意一定要加作为cache key,否则如果先请求的为head 类型,后面的GET清求返回为空。

PHP优化

  1. nginx配置
[root@web01 ~]# vim /etc/nginx/conf.d/php.conf
server {
    listen 80;
    server_name linux.php.com;
    root /code/php;
    index index.php;

    location ~* \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
  1. PHP页面配置
[root@web01 ~]# vim /code/php/index.php 
<?php
    phpinfo();

php.ini配置文件优化

#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#; Error handling and logging ;
#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
expose_php = Off                        # 关闭php版本信息
display_error = Off                     # 屏幕不显示错误日志(开发环境可以开启 on)
error_reporting = E_ALL                 # 记录PHP的每个错误
log_errors = On                         # 开启错误日志
error_log = /var/log/php_error.log      # 错误日志写入的位置(程序处理代码时的错误)
date.timezone = Asia/Shanghai           # 调整时区,默认PRC

#;;;;;;;;;;;;;;;;
#; File Uploads ;
#;;;;;;;;;;;;;;;;
file_uploads = On           # 允许文件上传
upload_max_filesize = 300M  # 允许上传文件的最大大小
post_max_size = 300M        # 允许客户端单个POST请求发送的最大数据
max_file_uploads = 20       # 允许同时上传的文件的最大数量
memory_limit = 128M         # 每个脚本执行最大内存

PHP危险函数禁用

禁用方法如下:

[root@web01 ~]# sed -i '/disable_functions/c disable_functions = phpinfo,eval,passthru,exec,system,chroot,scandir,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_alter,ini_restore,dl,p' /etc/php.ini
phpinfo() 
功能描述:输出 PHP 环境信息以及相关的模块、WEB 环境等信息。 
危险等级:中

passthru() 
功能描述:允许执行一个外部程序并回显输出,类似于 exec()。 
危险等级:高

exec() 
功能描述:允许执行一个外部程序(如 UNIX Shell 或 CMD 命令等)。 
危险等级:高

system() 
功能描述:允许执行一个外部程序并回显输出,类似于 passthru()。 
危险等级:高

chroot() 
功能描述:可改变当前 PHP 进程的工作根目录,仅当系统支持 CLI 模式 
PHP 时才能工作,且该函数不适用于 Windows 系统。 
危险等级:高

scandir() 
功能描述:列出指定路径中的文件和目录。 
危险等级:中

chgrp() 
功能描述:改变文件或目录所属的用户组。 
危险等级:高

chown() 
功能描述:改变文件或目录的所有者。 
危险等级:高

shell_exec() 
功能描述:通过 Shell 执行命令,并将执行结果作为字符串返回。 
危险等级:高

proc_open() 
功能描述:执行一个命令并打开文件指针用于读取以及写入。 
危险等级:高

proc_get_status() 
功能描述:获取使用 proc_open() 所打开进程的信息。 
危险等级:高

error_log() 
功能描述:将错误信息发送到指定位置(文件)。 
安全备注:在某些版本的 PHP 中,可使用 error_log() 绕过 PHP safe mode, 
执行任意命令。 
危险等级:低

ini_alter() 
功能描述:是 ini_set() 函数的一个别名函数,功能与 ini_set() 相同。 
具体参见 ini_set()。 
危险等级:高

ini_set() 
功能描述:可用于修改、设置 PHP 环境配置参数。 
危险等级:高

ini_restore() 
功能描述:可用于恢复 PHP 环境配置参数到其初始值。 
危险等级:高

dl() 
功能描述:在 PHP 进行运行过程当中(而非启动时)加载一个 PHP 外部模块。 
危险等级:高

pfsockopen() 
功能描述:建立一个 Internet 或 UNIX 域的 socket 持久连接。 
危险等级:高

syslog() 
功能描述:可调用 UNIX 系统的系统层 syslog() 函数。 
危险等级:中

readlink() 
功能描述:返回符号连接指向的目标文件内容。 
危险等级:中

symlink() 
功能描述:在 UNIX 系统中建立一个符号链接。 
危险等级:高

popen() 
功能描述:可通过 popen() 的参数传递一条命令,并对 popen() 所打开的文件进行执行。 
危险等级:高

stream_socket_server() 
功能描述:建立一个 Internet 或 UNIX 服务器连接。 
危险等级:中

putenv() 
功能描述:用于在 PHP 运行时改变系统字符集环境。在低于 5.2.6 版本的 PHP 中,可利用该函数 
修改系统字符集环境后,利用 sendmail 指令发送特殊参数执行系统 SHELL 命令。 
危险等级:高

php-fpm.conf配置文件优化

[root@web01 ~]# vim /etc/php-fpm.conf
[global]
;pid = /var/log/php-fpm/php-fpm.pid         # pid文件存放的位置
;error_log = /var/log/php-fpm/php-fpm.log   # 错误日志存放的位置(启动时的日志)
;log_level = error                  	    # 日志级别, alert, error, warning, notice, debug
rlimit_files = 65535                	    # php-fpm进程能打开的文件句柄数
;events.mechanism = epoll           	    # 使用epoll事件模型处理请求
include=/etc/php-fpm.d/*.conf

php-fpm.conf包含配置文件优化

[root@web01 ~]# vim /etc/php-fpm.d/www.conf
[www]       				                      # 池名称
user = www  				                      # 进程运行的用户
group = www 				                      # 进程运行的组
;listen = /dev/shm/php-fpm.sock                   # 监听在本地socket文件
listen = 127.0.0.1:9000                           # 监听在本地tcp的9000端口
;listen.allowed_clients = 127.0.0.1               # 允许访问FastCGI进程的IP,any不限制 
pm = dynamic                                      # 管理方式(dynamic为动态,static为静态)
pm.max_children = 512                             # 最大启动的php-fpm进程数(静态管理,配置dynamic时失效)
pm.start_servers = 32                             # 动态方式下的起始php-fpm进程数量。
pm.min_spare_servers = 32                         # 动态方式下的最小php-fpm进程数量。
pm.max_spare_servers = 64                         # 动态方式下的最大php-fpm进程数量。
pm.max_requests = 1500                            # 达到这个请求数,子进程会重启,如果是0那就一直接受请求
pm.process_idle_timeout = 15s;                    # 没有请求时多久释放一个进程
pm.status_path = /phpfpm_status                   # 开启php的状态页面

php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/phpfpm_error.log
php_admin_flag[log_errors] = on
request_slowlog_timeout = 5s                      # php脚本执行超过5s的文件
slowlog = /var/log/php_slow.log                   # 记录至该文件中

php状态页

1)php配置

[root@web01 ~]# vim /etc/php-fpm.d/www.conf
pm.status_path = /phpfpm_status 		# 开启php的状态页面

2)nginx配置

[root@web01 ~]# cat /etc/nginx/conf.d/php.conf
server {
	listen 80;
	server_name linux.php.com;
	root /code/php;
	index index.php;
	location ~* \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
	}
	location /status {
		stub_status;
	}
	location /php_status {
		fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
	}	
}

3)浏览器访问http://linux.php.com/php_status

pool:                 www				# 池名称
process manager:      dynamic		    # 动态管理
start time:           14/Sep/2020:18:52:12 +0800	# 启动时间
start since:          14				# 启动了多久
accepted conn:        1					# 连接数
listen queue:         0					# 等待队列
max listen queue:     0					# 最大等待队列
listen queue len:     511				# 等待队列长度
idle processes:       4					# 空闲的进程数
active processes:     1					# 活跃的进程数
total processes:      5					# 总的进程数
max active processes: 1					# 最大的活跃进程数
max children reached: 0					# 进程最大的限制连接数
slow requests:        0					# 慢查询
posted @ 2021-06-05 11:09  原因与结果  阅读(262)  评论(1编辑  收藏  举报