017.Nginx性能优化

017.Nginx性能优化

性能优化概述

基于Nginx的性能优化首先需要对系统和Nginx当前的运行状态做一个信息收集,例如了解当前系统的结构和瓶颈,了解当前系统运行的业务、服务,单个服务能够支撑多大并发、最高瓶颈是多少、支持多少qps(每秒查询率)的访问请求,通过一些系统监控工具和压力测试工具,获取当前系统架构能够承受多少的请求和并发,基于此数据做相应的性能评估和优化方向

其次需要了解业务模式,每一个性能优化的目的都是为业务提供服务,所以需要了解业务接口的类型、系统层次化的结构,比如电商网站的抢购模式,只会在某一段时间内流量突增、比如Nginx所代表的角色,是代理、动静分离还是后端服务,针对不同的场景提供不同的优化方向

最后需要考虑性能和安全的对比,性能与安全在一定程度上是对立的,安全检测严密会对性能产生影响,过度追求性能会导致安全隐患,所以设计防火墙功能时必须平衡好两者的关系

压力测试工具

在系统业务量没有增长前就需要做好相应准备,以防范业务量突增带来的接口压力,因此对于业务接口的压力测试就显得非常重要,在业务上线之前就需要先对业务接口进行请求和并发的测试。管理员需要对系统能够承受的压力有一个评估,然后通过工具检测系统是否能够满足对应压力的需求

  1. 安装ab压力测试工具
[root@web01 ~]# yum install -y httpd-tools
  1. ab压测工具的使用方式
ab -n 200 -c 2 http://127.0.0.1/
    -n:总的请求次数
    -c:并发请求数
    -k:是否开启长链接
    -s:最大超时时间,默认30s
  1. 安装tomcat
[root@web01 ~]# yum install -y java
[root@web01 ~]# wget --no-check-certificate https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.73/bin/apache-tomcat-9.0.73.tar.gz
[root@web01 ~]# tar -xzf apache-tomcat-9.0.73.tar.gz
[root@web01 ~]# mv apache-tomcat-9.0.73 /usr/local/tomcat
[root@web01 ~]# /usr/local/tomcat/bin/startup.sh    # 启动tomcat

yum安装的java版本较低,只能配合tomcat9,tomcat10会因为java版本问题启动失败

  1. 配置Nginx静态网站与tomcat动态网站环境
[root@web01 ~]# vim /usr/local/nginx/conf.d/ab.example.com.conf
server {
    server_name ab.example.com;
    listen 80;
    location / {
        root /usr/local/nginx/html/;
        index index.jsp index.html;
        try_files $uri @java_page;
    }
    location @java_page {
        proxy_pass http://127.0.0.1:8080;
    }
}
[root@web01 ~]# echo "Nginx Ab Load" > /usr/local/nginx/html/test7.html
[root@web01 ~]# echo "Tomcat Ab Load" > /usr/local/tomcat/webapps/ROOT/test7.html
  1. Nginx站点压力测试
[root@web01 ~]# curl http://127.0.0.1/test7.html    # 查看当前是Nginx站点还是Tomcat站点
[root@web01 ~]# ab -n10000 -c200 http://127.0.0.1/test7.html
...
Server Software:        nginx/1.22.1
Server Hostname:        127.0.0.1
Server Port:            80

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

Concurrency Level:      200     请求并发数
Time taken for tests:   2.260 seconds       处理完所有请求所用的总时长
Complete requests:      10000       总请求数
Failed requests:        0       失败的请求数
Write errors:           0       
Total transferred:      2630000 bytes       总传输大小
HTML transferred:       140000 bytes        HTML传输字节大小
Requests per second:    4423.96 [#/sec] (mean)      QPS,每秒请求数,意味着每秒需要处理多少请求
Time per request:       45.208 [ms] (mean)      客户端请求服务端时,每个请求需要耗费的时间
Time per request:       0.226 [ms] (mean, across all concurrent requests)       服务端处理客户端请求时,每个请求的处理时间
Transfer rate:          1136.23 [Kbytes/sec] received       传输速率
...

# 将html文件移走后再测试
[root@web01 ~]# mv /usr/local/nginx/html/test7.html /opt/
[root@web01 ~]# ab -n10000 -c200 http://127.0.0.1/test7.html
...
Non-2xx responses:      10000       # 测试结果多出一行非200系列响应码,请求回应总数
...
  1. Tomcat站点压力测试

在测试Tomcat站点前,必须将Nginx站点的html文件移走,否则用户请求仍会由Nginx解析

[root@web01 ~]# curl http://127.0.0.1/test7.html    # 确保此时是Tomcat站点反馈
[root@web01 ~]# ab -n10000 -c200 -k http://127.0.0.1/test7.html
Server Software:        nginx/1.22.1
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /test7.html
Document Length:        15 bytes

Concurrency Level:      200
Time taken for tests:   70.185 seconds
Complete requests:      10000
Failed requests:        208
   (Connect: 0, Receive: 0, Length: 76, Exceptions: 132)
Write errors:           0
Total transferred:      2700000 bytes
HTML transferred:       150000 bytes
Requests per second:    142.48 [#/sec] (mean)
Time per request:       1403.696 [ms] (mean)
Time per request:       7.018 [ms] (mean, across all concurrent requests)
Transfer rate:          37.57 [Kbytes/sec] received

[root@web01 ~]# ss -an  # 在压力测试下查看端口信息

Tomcat站点测试需要设置超时时间或长链接,否则ab命令可能会因为超时报错。在长链接场景下会产生大量的TIME_WAIT状态,每个TIME_WAIT都会占用一个端口,在大量请求、高并发的情况下,站点可能会因为端口用尽而无法响应用户请求;对比Nginx和Tomcat的测试可以非常明显观察到,Nginx的静态资源响应速率远超Tomcat

系统性能优化

Linux中所有资源都以文件的形式存在,比如消息、共享内存、连接等,句柄可以理解为指向这些文件的指针。文件句柄会随着进程的调用频繁增加,系统默认文件句柄是有限制的,不能让一个进程无限制的调用,所以管理员需要限制每个进程和每个服务使用多大的句柄,此为必要的优化调整参数。文件句柄有3种设置方式:系统全局修改、用户局部修改、进程局部修改

[root@web01 ~]# ulimit -a   # 查看单个进程可打开的句柄上限
[root@web01 ~]# ulimit -a PID    # 查看某个进程的句柄上限
[root@web01 ~]# ulimit -n   # 查看系统设置的最大文件句柄数

# 针对root用户,soft仅提醒,hard限制,nofile打开最大文件数
[root@web01 ~]# vim /etc/security/limits.conf
root soft nofile 65535
root hard nofile 65535

# *代表所有用户
[root@web01 ~]# vim /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
* - nofile 65535    # -代表soft和hard两者兼具

# 针对Nginx进程
[root@web01 ~]# vim /usr/local/nginx/conf.d/ab.example.com.conf
worker_rlimit_nofile 65535  # 在nginx核心模块加入此选项

使用lsof监控文件描述符数量

[root@web01 ~]# lsof -i :80 # 查看监听80端口的进程
[root@web01 ~]# lsof -p $(more /usr/local/nginx/nginx.pid) | wc -l  # 统计nginx打开的文件描述符

# ab压力测试的同时检测nginx的worker进程占用的文件描述符数量
[root@web01 ~]# ab -k -n 10000 -c 200 http://127.0.0.1/test7.html
[root@web01 ~]# lsof -p 663 | wc -l

调整内核参数,使time_wait状态端口复用

[root@web01 ~]# vim /etc/sysctl.conf
...
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1 # 打开时间戳
...
[root@web01 ~]# sysctl -p   # 从指定文件中加载内核参数,若未指定文件则从/etc/sysctl.conf中加载;此命令也可用于查看管理员手动添加的内核参数
[root@web01 ~]# sysctl -a   # 查看系统设置的默认的内核参数

代理服务优化

通常Nginx作为代理服务负责转发用户请求,那么转发的过程中开启HTTP长链接,能够减少握手次数、降低服务器损耗

  1. 长链接语法示例(应用层优化)
Syntax: keepalive connections;
Default: -
Context: upstream
This directive appeared in version 1.1.4

connections的值代表着连接到upstream服务器的空闲长连接的最大数量,它的主要作用是设定每个Nginx的单个worker进程对于upstream中的server的最大空闲连接数。keepalive指令并不会限制Nginx的所有worker进程能够开启的连接到upstream服务器的连接总数;如果这个值设置过大,会导致过多的空闲连接占满了upstream中的server资源,长时间的TCP连接容易导致系统资源无效占用、如果这个值设置过小,在高QPS场景会产生大量连接被生成再被抛弃的情况,也就是大量的TIME_WAIT。因此这个数值的设定需要根据worker进程数量来调整

  1. 配置Nginx代理服务使用长链接方式

在正常配置upstream的情况下,代理与后端服务器是不会保持长链接的,此前代理篇章有一个参数proxy_http_version 1.1;修改http协议版本为1.1,此参数能够让代理节点连接后端节点保持长链接,但这个长链接是针对用户请求的

从HTTP协议的角度看,Nginx代理在这个过程中,对于客户端它扮演着HTTP服务器端的角色。而对于真正的服务器端(在nginx的术语中称为upstream)Nginx代理又扮演着HTTP客户端的角色,keepalive指令出现在版本1.1.4,如果此时代理节点想要长期与后端节点保持长链接,就需要在upstream区块下添加keepalive参数

[root@lb01 ~]# more /etc/nginx/conf.d/proxy_optimize.conf 
upstream optimize {
        server 172.16.1.7:8080;
        keepalive 16;
}

server {
        listen 80;
        server_name _;
        charset utf-8,gbk;
        location / {
                proxy_pass http://optimize;
                proxy_set_header Connection ""; # 消除“connection”头字段,connection字段通过浏览器调试模式下能够看到值
                include proxy_params;   # 此文件内的参数也是优化参数
        }
}

keepalive 16表示最大保持16个空闲长链接,一个keepalive连接上默认最大能够处理100个请求,可以通过keepalive_requests参数修改设置。长链接默认存在一个超时时间,在负载较低的情况下,某个长链接在超时时间内如果没有任何请求,服务端节点会向客户端(代理节点)发起长链接断开,并将该链接的状态修改为TIME_WAIT,而代理节点设置了keepalive参数后不会断开长链接,也就导致服务端节点上会最多维持16个TIME_WAIT状态链接,这个TIME_WAIT状态也不会一直维持,在一段时间后断开

keepalive说明

  1. 对于fastcgi服务器,需要设置fastcgi_keep_conn以便保持长连接
[root@web01 ~]# vim /usr/local/nginx/conf.d/lnmp.example.com.conf 
upstream fastcgi_backend {
        server 127.0.0.1:9000;
        keepalive 8;
}

server {
        listen 80;
        server_name lnmp.example.com;
        root /usr/local/nginx/html/lnmp/;
        location / {
                index test.php;
        }
        location ~ \.php$ {
                fastcgi_pass fastcgi_backend;
                fastcgi_keep_conn on;   # fastcgi启用长链接
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }
}

此处fastcgi语法看起来可能有些奇怪,此前fastcgi并没有与代理语法联用过,但keepalive选项只能写在upstream区块中。scgi和uwscgi协议没有保持连接的概念,但无论是proxy、fastcgi、uwsgi协议都有cache缓存的功能。proxy的cache只能缓存静态资源,fastcgi能够缓存动态资源,无论是那种缓存都需要根据实际情况考量是否开启,缓存数据保留在代理节点的本地磁盘中,在本地磁盘性能较弱的情况下,开启缓存甚至可能降低站点的运行效率

  1. 扩展命令

keepalive_requests,设置通过一个keepalive连接提供的最大请求数。超出最大请求数后关闭连接

Syntax: keepalive_requests number;
Default: keepalive_requests 100;
Context: upstream
# 该指令出现在1.15.3版本中

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

Syntax: keepalive_timeout timeout;
Default: keepalive_timeout 60s;
Context: upstream
# 该指令出现在1.15.3版本中

静态资源优化

前文内容是对Nginx的应用服务优化,如代理、动态资源访问。当Nginx作为静态资源Web服务器,用于处理静态资源时传输非常高效,这里的静态资源指的是非Web服务端运行处理而生成的文件

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

静态资源缓存

静态资源缓存指的是浏览器的缓存,浏览器缓存设置用于提高站点性能,例如新闻类网站,图片一旦发布,改动的可能是非常小的。如果此时图片能在用户的浏览器上长期缓存,势必能够提升用户访问站点的效率。浏览器有自身的缓存机制,它基于HTTP协议缓存机制实现,在HTTP协议中有很多头信息,实现浏览器的缓存就需要依赖特殊的头信息来与服务器进行特殊验证,如Expires(http/1.0);cache-control(http/1.1)

浏览器缓存

浏览器缓存过期校验机制

  1. 浏览器请求服务器会先进行Expires、Cache-Control的检查,检查缓存是否过期,如果没有过期则直接从缓存文件向用户呈现站点页面
  2. 如果缓存过期,首先检查是否存在Etag。如果存在则客户端会携带if-None-Match头信息向web服务器请求Etag值,服务端根据计算出的Etag值与if-None-Match值对比是否一致,如果值一致则由服务器决策返回200还是304
  3. 如果Etag不存在,则进行last-Modified检查,客户端会携带if-Modified-Since头信息向Web服务端请求last-Modified时间,与if-Modified-Since进行对比,如果两者值一致则由服务器决策返回200还是304

if-None-Match的值就是浏览器首次访问Web服务端时,服务端向浏览器返回的Etag值

浏览器缓存调用流程

if-None-Match类似于对站点内容的校验值,在缓存过期的情况下向Web节点请求Etag值,如果两者对比值是相同的,则说明站点内容未修改;if-Modified-Since表示站点文件的最后修改时间,在Etag标签不存在的情况下,对比站点文件最后的修改时间,如果if-Modified-Since时间与last-Modified时间一致,说明站点文件还未修改过;Etag是首要验证方式,如果Etag验证无误则不会再验证last-Modified

无论使用哪种方式校验浏览器缓存,其目的都是为了对比浏览器缓存数据是否与Web服务器站点数据一致。无论缓存对比结果如何,最终都还是需要Web服务器来决策是否使用浏览器缓存呈现给用户

浏览器缓存校验关键词

在图中可以很明显看到Web服务器使用浏览器缓存呈现给用户,响应码是304重定向。浏览器有个默认的参数Cache-Control: max-age=0,这个参数表示浏览器不缓存,也就是说反复的请求Web服务端的过程中,每一次的请求都是过期的、每一次请求浏览器都要与Web服务端做一次交互,校验Etag。校验Etag后得出的结论是站点数据未修改,Web服务端决策直接从浏览器缓存呈现数据,所以浏览器持续请求得到的响应码会持续是304

配置静态资源缓存

默认浏览器不会缓存站点数据,可以通过nginx的配置添加HTTP头信息

  1. 指令
# 作用:添加Cache-Control Expires头
Syntax: expires [modified] time;
expires epoch | max | off;
Default: expires off;
Context: http, server, location, if in location
  1. 示例
[root@web01 ~]# vim /usr/local/nginx/conf.d/test8.example.com.conf
server {
        listen 80;
        server_name _;
        charset utf-8,gbk;
        root /usr/local/nginx/html/;
        location / {
                index index.html;
        }
        location ~ \.(png|jpg|gif|jpeg)$ {
                expires 1d;
        }
}

修改nginx配置后再访问Web服务端,从浏览器的调试界面能够看到Cache-Control的值已经修改并多出一个Expires头信息。如果不想浏览器持续响应304,可以勾选浏览器设置“Disable cache(停用缓存)”,勾选后浏览器参数值发生改变Cache-Control: no-cache,再次刷新浏览器会从Web服务端获取站点,响应码200;Cache-Control: max-age=0默认单位为秒

补充:自定义Nginx头信息

location ~.(png|jpg|gif|jpeg)$ {
expires 10s;
add_header X-Cache $host; # 自定义头信息X-Cache,名称可更改,$host是nginx的内置变量,可更改为其他变量
}

  1. 设置静态文件不缓存
[root@web01 ~]# vim /usr/local/nginx/conf.d/test8.example.com.conf
...
location ~ \.(png|jpg|gif|jpeg)$ {
        #       expires 10s;    # 避免冲突
                add_header Cache-Control no-store;
                add_header Pragma no-cache;
        }

连续请求网页查看响应码,持续响应200表示不缓存生效

静态资源读取

  1. 文件高效读取sendfile
Syntax: sendfile on | off;
Default: sendfile off;
Context: http, server, location, if in location

sendfile

编译安装的nginx默认开启sendfile选项

  1. 数个数据包一次发送,用于提升网络效率tcp_nopush;大文件推荐开启,此参数前提是需要开启sendfile
Syntax: tcp_nopush on | off;
Default: tcp_nopush off;
Context: http, server, location
  1. 提高网络传输实时性tcp_nodeplay;keepalive
Syntax: tcp_nodelay on | off;
Default: tcp_nodelay on;
Context: http, server, location

tcp_nopush与tcp_nodelay两者作用略微冲突,建议不要同时开启。tcp_nopush与sendfile联用,能够提升网络利用率、tcp_nodeplay与keepalive联用,能够保障数据传输的实时性

静态资源压缩

Nginx将响应报文发送至客户端之前,对报文进行压缩后传输能够有效节省带宽资源,并提高响应到客户端的速度

  1. 传输前压缩、传输后解压,传输压缩gzip
Syntax: gzip on | off;
Default: gzip off;
Context: http, server, location

浏览器对于报文的解压需要依托于CPU,会占用客户端CPU资源,只不过资源开销不大

  1. gzip压缩资源类型
Syntax: gzip_types mime-type ...;
Default: gzip_types text/html;
Context: http, server, location

gzip可压缩的资源类型在nginx的安装目录下的mime.types文件中,此文件内的所有类型资源都可以通过gzip压缩

  1. gzip压缩比率
Syntax: gzip_comp_level level;
Default: gzip_comp_level 1;
Context: http, server, location

提高gzip的压缩比率能有效提高网络传输效率,但压缩本身比较耗费服务器性能

  1. gzip压缩协议版本
Syntax: gzip_http_version 1.0 | 1.1;
Default: gzip_http_version 1.1;
Context: http, server, location

压缩协议版本主要用于选择http协议的版本,主流选择http 1.1

  1. 静态图片压缩示例
[root@web01 ~]# vim /usr/local/nginx/conf.d/test8.example.com.conf
...
location ~ \.(png|txt)$ {
                gzip on;
                gzip_http_version 1.1;
                gzip_types image/png text/plain;
                gzip_comp_level 5;
        }
...
[root@web01 ~]# ll -dh /var/log/nginx/access.log-20230326
-rw-r----- 1 nginx adm 46K Mar 26 03:16 /var/log/nginx/access.log-20230326
[root@web01 ~]# cp /var/log/nginx/access.log-20230326 /usr/local/nginx/html/log.txt

gzip压缩对于压缩图片似乎不友好,压缩文件的效果比较好,但必须要能够匹配上location规则才能够正确压缩文件

静态资源防盗链

防盗链指的是防止资源被其他网站恶意盗用。在某些情况下,一些站点通过引用的方式将我方站点的优质资源展示在他人站点上,例如高清图片,而不是下载资源保存在他人站点本地,这会导致用户在浏览他人站点的时候,导致我站流量流失。甚至如果他人站点遭到恶意攻击会造成我站流量损失严重

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

Syntax: valid_referers none | blocked | server_names | string ...;
Default: -;
Context: server, location
# none: Referer来源头部为空的情况
# blocked: Referer来源头部不为空,但这些值都不以http://或https://开头
# server_names: 来源头部包含当前域名,可以正则匹配
  1. 在盗链站点上准备html文件,偷取防盗链站点的图片
[root@web02 ~]# vim /usr/local/nginx/html/chain.html
<html>
<head>
        <meta charset="utf-8" />
        <title>chain theft</title>
</head>
<body style="background-color:red;">
        <img src="http://172.16.1.7/nginx.png" alt=""/>
</body>
</html>

使用浏览器访问盗链站点,查看图片是否能够正常访问

  1. 在防盗链站点上启动基于Referer的防盗链
[root@web01 ~]# vim /usr/local/nginx/conf.d/test8.example.com.conf
...
location ~ \.(jpg|png|gif|jpeg)$ {
    # 指定合法的来源referer,valid_referers下的所有值被设置为0,否则设置为1
    valid_referers none blocked *.example.com;
    if ($invalid_referer) {
        return 403;
        #rewrite ^(.*)$ /chain_theft/advertisement.jpg break;
    }
}

此配置表示所有来自example.com的请求可以访问到当前站点图片,如果来源域名不在valid_referers这个列表中,那么$invalid_referer等于1,在if语句中返回一个403响应码;此时再此访问盗链站点已经无法正常盗链,返回403只是一个示例,在实际配置中也可以选择给盗链站点返回一个我站广告图片。换言之,如果想要资源可以被引用,使用白名单,否则,使用广告页

  1. 希望某些网站能够使用盗链资源

例如谷歌、百度这种搜索引擎引用我站信息更容易推广站点

location ~ \.(jpg|png|gif|jpeg)$ {
    valid_referers none blocked *.example.com server_names ~\.google\. ~\.baidu\.;
    if ($invalid_referer) {
        return 403;
    }
}
  1. 这种防护不能绝对保证资源被盗链,可以通过命令或程序修改来源referer信息
[root@web02 ~]# curl -I http://172.16.1.8/test8.html    # 仅查看Header信息
[root@web02 ~]# curl -I http://172.16.1.8/test8.html    # 查看html文件主体
[root@web02 ~]# curl -e "http://www.baidu.com" -I http://172.16.1.7/nginx.png  # 伪造referer头信息访问被盗链站点

使用curl直接访问http://172.16.1.7/nginx.png是可以成功的,因为盗链nginx配置文件中加入了none这个选项,代表来源头部为空的请求可以访问

跨域访问

当用户通过浏览器访问A网站时,会利用到ajax或其他方式同时请求B网站,这就会出现请求一个页面使用了2个域名,这就是跨域访问。浏览器默认禁止跨域访问,请求一个页面只能使用一个域名,正常来说这是浏览器的功能,那Nginx允许跨站访问与浏览器有什么关系呢?因为浏览器会读取Access-Control-Allow-Origin的头信息,如果服务端允许则浏览器不会进行拦截

Syntax: add_header name value [always];
Default: -;
Context: http, server, location, if in location
  1. 在web01上准备跨站访问的html文件
[root@web01 ~]# vim /usr/local/nginx/conf.d/test8.example.com.conf
server {
        listen 80;
        server_name _;
        charset utf-8,gbk;
        root /usr/local/nginx/html/;
        location / {
                index station.html;
        }
}

[root@web01 ~]# vim /usr/local/nginx/html/station.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://web02.example.com/cross/station.html",
        success: function(data) {
            alert("sucess.");
        },
        error: function() {
            alert("fail.");
        }
    });
});
</script>
<body>
    <h1>测试跨域访问</h1>
</body>
</html>

[root@web01 ~]# vim /etc/hosts
172.16.1.8      web02.example.com
  1. 在web02节点准备站点
[root@web02 ~]# vim /usr/local/nginx/conf.d/station.example.com.conf
server {
        listen 80;
        server_name web02.example.com;
        charset utf-8,gbk;
        location / {
                root /usr/local/nginx/html/;
                index index.html;
        }
}

[root@web02 ~]# mkdir /usr/local/nginx/html/cross
[root@web02 ~]# echo "cross station test" > /usr/local/nginx/html/cross/station.html
  1. 测试跨域访问

直接测试跨域访问失败

测试跨域访问

添加web02节点跨域访问配置信息

location ~ .*\.(html|htm)$ {
    add_header Access-Control-Allow-Origin http://web02.example.com;    # 站点白名单,可以使用通配符
    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;    # 跨域访问可以做的操作
}

使用本地虚拟机测试跨域访问可能无法实现效果,访问百度或阿里云等站点能够找到某些文件,是允许任何站点跨域访问的,通过这些链接可以测试虚拟机的跨域访问是否成功;或者也可以在本地虚拟机Web02节点设置允许所有主机跨域访问,两种方式是等效的

CPU亲和

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

[root@web01 ~]# lscpu
...
CPU(s):                2
Thread(s) per core:    2
Core(s) per socket:    1
Socket(s):             1
NUMA node0 CPU(s):     0,1
...

机器有1个CPU插槽(Socket),插槽上的核心数(Core(s) per socket)为1,每个核心线程数(Thread(s) per core)为2,所以总的逻辑CPU(CPU(s))数为2

  1. 将Nginx worker进程绑定至不同的核心上,官方建议与CPU的核心保持一致
# 第一种绑定组合方式
worker_processes 24;
worker_cpu_affinity 000000000001 000000000010 000000000100 000000001000 000000010000 000000100000 000001000000 000010000000 000100000000 001000000000 010000000000 10000000000;

# 第二种方式
worker_processes 2;
worker_cpu_affinity 101010101010 010101010101;

# 最佳方式绑定方式
worker_processes auto;
worker_cpu_affinity auto;

一、二种方式是比较旧的亲和配置,到现在为止应建议使用auto配置

  1. 查看nginx worker进程绑定至对应cpu
[root@web01 ~]# vim /usr/local/nginx/nginx.conf
worker_processes  auto;
worker_cpu_affinity auto;
...
[root@web01 ~]# ps aux | grep "nginx"   # 查看nginx启动的worker进程数量
[root@web01 ~]# ps -eo pid,args,psr | grep [n]ginx  # 查看worker进程与CPU线程的绑定关系

Nginx通用的配置文件

cat nginx.conf
user  nobody;   # nginx worker进程用户
worker_processes  auto; # worker进程数
worker_cpu_affinity auto;   # CPU亲和
error_log /var/log/nginx/error.log warn;    # warn或以上级别的日志写入错误日志
pid /run/nginx.pid  # nginx主进程号文件
worker_rlimit_nofile 35535; # 每个worker进程能打开的文件描述符数量,调整至1w以上,负载较高建议2~3w

events {
    use epoll;  # 使用epoll网络模型
    worker_connections  10240;   # 限制每个worker进程能够处理的最大连接数,总连接数等于10240*[CPU核心数]
}

http {
    include       mime.types;
    default_type  application/octet-stream; # 默认下载类型
    charset utf-8,gbk;  # 统一字符集

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  logs/access.log  main;

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

    # 文件高效传输,静态资源服务器建议打开
    sendfile        on;
    tcp_nopush      on;
    # 文件实时传输,动态资源服务建议打开
    tcp_nodelay     on;
    keepalive_timeout  65;
    # Gzip压缩
    gzip            on;
    gzip_disable    "MSIE [1-6]\."; # 针对不支持压缩的浏览器类型,对于特定浏览器不执行压缩。更建议重定向到其他页面
    gzip_http_version   1.1;
    gzip_min_length 1024;   # 文件大小超过1024字节时压缩,否则不压缩。默认20字节
    # 虚拟主机
    include       conf.d/*.conf;
}

Nginx优化总结

  1. CPU亲和、worker进程数、调整每个worker进程能够打开的文件句柄数
  2. 使用epoll网络模型、调整每个worker进程的最大连接数
  3. 文件的高效读取sendfile、nopush
  4. 文件的实时传输nodelay、keepalive
  5. 开启TCP长链接、长链接超时时间keepalive
  6. 文件传输压缩gzip
  7. 静态文件缓存expires
  8. 隐藏Nginx的版本号
  9. 禁止通过IP地址访问、禁止恶意域名解析、仅允许域名访问
  10. 配置防盗链、跨域访问
  11. 抗DDOS、cc攻击,限制单IP并发连接、http请求
  12. 优雅展示nginx错误页面
  13. nginx加密传输https
  14. nginx proxy_cache、fastcgi_cache、uwsgi_cache缓存(Varnish)

PHP服务优化

php服务优化分两部分,一部分是由php.ini配置文件为主导的PHP解释器的优化,另一部分则是以php-fpm服务为主导的,关于PHP进程的优化

  1. PHP程序配置管理文件/usr/local/php/lib/php.ini,主要调整日志、文件上传、禁止危险函数、关闭版本号显示等
#Errorlogging 错误日志设置
expose_php = Off            # 关闭PHP版本信息;默认开启
display_error = Off         # 浏览器屏幕不显示错误日志;默认关闭
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  # 允许上传文件的最大容量;默认2M
post_max_size = 300M        # 允许客户端单个post请求发送的最大数据;默认8M
max_file_uploads = 20       # 允许同时上传的文件的最大数量;默认20
memory_limit = 128M         # 每个脚本执行的最大内存;默认128M

#session 会话共享
session.save_handler = redis
session.save_path = "tcp://172.16.1.51:6379"

#PHP禁止危险函数执行
disable_functions = phpinfo # 默认为空值

PHP危险函数

  1. php-fpm进程管理配置文件/usr/local/php/etc/php-fpm.conf

php-fpm服务的主配置文件php-fpm.conf中的内容基本全部注释了,最主要的一项配置就是include=/usr/local/php/etc/php-fpm.d/*.conf,所以真正需要优化php-fpm服务的配置文件都在/usr/local/php/etc/php-fpm.d/这个目录下

# 第一部分,fpm配置
include=etc/php-fpm.d/*.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;默认日志级别是notice
rlimit_files = 65535    # php-fpm进程能够打开的文件句柄数
events.mechanism = epoll    # 使用epoll时间模型处理请求

# 第三部分,进程池定义
[www]   # 进程池名称
user = nobody   # 进程运行的用户
group = nobody  # 进程运行的组
;listen = /dev/shm/php-fpm.sock    # 监听本地socket文件
listen = 127.0.0.1:9000
;listen.allowed_clients = 127.0.0.1,172.16.1.8   # 允许白名单连接到本地的PHP程序;默认仅允许本机连接

pm = dynamic    # 选择进程管理器控制子进程数量的方式,共有3种方式static、dynamic、ondemand。关于3种方式的解析,php-fpm.conf配置文件的注解都有解释
pm.max_children = 512    # 最大能够启动的进程数
pm.start_servers = 32    # 启动php-fpm服务时启动5个进程
pm.min_spare_servers = 32    # 处于空闲状态(等待处理)的最少进程数,如果空闲进程数小于这个数量,那么将创建一些子进程
pm.max_spare_servers = 64   # 处于空闲状态(等待处理)的最大进程数
pm.process_idle_timeout = 15s   # 空闲进程被终止的等待时间,超过这个时间的空闲进程会被杀死,直到最小进程数
pm.max_requests = 1500  # 每一个进程能响应的请求数

# 第四部分,日志
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
# 慢日志
request_slowlog_timeout = 5s    # php脚本执行超过5s的文件
slowlog = /var/log/php_slow.log # 慢日志文件路径
  1. php-fpm监控模块,用于监控php-fpm状态使用
vim /usr/local/php/etc/php-fpm.d/www.conf
pm.status_path = /status    # 开启php的状态页面
vim /usr/local/nginx/conf.d/php.example.com.conf
...
location /status {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}
...
curl http://php.example.com/status

pool:                 www   # fpm池名称,大多为www
process manager:      dynamic   # 动态管理phpfpm进程
start time:           13/Apr/2023:10:03:05 +0800    # 启动时间,如果重启会发生变化
start since:          191   # php-fpm运行时间
accepted conn:        1     # 当前池接受的连接数
listen queue:         0     # 请求等待队列,如果这个值不为0,则需要增加fpm的进程数
max listen queue:     0     # 请求等待队列最高的数量
listen queue len:     128   # 请求队列的长度
idle processes:       1     # php-fpm空闲的进程数
active processes:     1     # php-fpm活跃的进程数
total processes:      2     # php-fpm总的进程数
max active processes: 1     # php-fpm最大活跃的进程数(FPM启动开始计算)
max children reached: 0     # 限制过进程最大数量的次数,如果数量不为0,则说明php-fpm最大进程数较小,可以适当调整
slow requests:        0

优化总结

nginx

结构层次 角色&作用
硬件层面 代理比较消耗CPU、内存;静态比较消耗磁盘I/O
网络层面 网络带宽大小、传输速率、是否丢包
系统层面 调整文件句柄、timewait重用
应用层面 nginx作为代理、keepalive长链接
服务层面 nginx作为静态,浏览器缓存、文件传输、压缩、防盗链、跨域访问、CPU亲和; nginx作为缓存,proxy_cache、fastcgi_cache、uwsgi_cache; nginx作为安全,nginx+lua实现waf防火墙

php

php.ini 错误日志记录、文件大小调整、session会话共享设置、禁止危险函数
php-fpm 监听地址、进程数动态调节、开启日志
php状态 php自身监控的状态信息
php慢查询 记录具体时间、哪个进程、执行的哪个脚本文件、哪个函数、第几行达到了超时时间
posted @ 2023-06-19 09:24  hebo  阅读(172)  评论(0)    收藏  举报