一文玩转NGINX(对于NGINX,你真的了解吗?)
前言
nginx [engine x] 是一个 HTTP 和反向代理服务器, 邮件代理服务器, 和一个通用的 TCP/UDP 代理服务器, 最初由 Igor Sysoev 。 运行了很久 在许多负载重的俄罗斯网站上,包括 雅得士 , 邮件.Ru , 和 _ 漫步者 。 根据 Netcraft 的说法,nginx 服务或代理 21.62% 2022 年 8 月最繁忙的站点
nginx常用于反向代理,负载均衡。也可作WAF(应用防火墙)等。
Nginx优势
对于NGINX而言,IO多路复用epoll为其优点之一。而NGINX的轻量级以及CPU亲和,sendfile(零拷贝),动静分离等,都是NGINX优势所在。
IO多路复用epoll\
IO多路复用:多个描述符的I/O操作都能在一个线程内并发交替地顺序完成,这里的"复用" 指的是复用同一个线程
epoll。IO多路复用的实现方式 select,poll,epoll。
关于Unix与IO,可以参考笔者的《关于网络I/O与并发》文章。
什么是IO复用?下方图示对此技术有较为直观的体现。IO复用通常指多个描述的I/O操作在一个线程内并发、交替、顺序完成。而复用,指的是同一个线程。
轻量级
NGINX轻量级体现于:
- 源代码只有核心代码,其它可以以插件的形式安装。
- 代码模块化,适合二次开发。
sendfile零拷贝
关于sendfile技术,建议参考笔者的《关于网络I/O与并发》文章,了解前置知识。
sendfile系统调用在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,操作效率很高,被称之为零拷贝。
sendfile() 利用 DMA 引擎,将文件数据拷贝到操作系统内核缓冲区,然后数据被拷贝到与 socket 相关的内核缓冲区。完成以上步骤后,DMA 引擎将数据从内核的 socket 缓冲区拷贝到协议引擎中去。
相比于传统I/O,无需再将数据从页缓存写道磁盘上,可以直接再以后公用程序地址空间和磁盘之间进行数据传输。无需占用大量的CPU与内存,即可完成数据传输。
CPU亲和
cpu的亲和能够使nginx对于不同的work工作进程绑定到不同的cpu上面去。就能够减少在work间不断切换cpu,把进程通常不会在处理器之间频繁迁移,进程迁移的频率小,来减少性能损耗。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ngGDzotr-1663225422004)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bd52db11abf74fc6a6a4c558b1411138~tplv-k3u1fbpfcp-watermark.image?)]
动静分离
对于NGINX动静分离,简单描述就是将动态请求与静态请求分开。
目前对于动静分离通常为两种,一种是将静态文件使用单独的域名,部署在独立的服务器上,也是目前最主流推崇的方案。而另一种方案则是将动静态文件混合发布,使用NGINX将其分开。\
对于动静分离,我们可以使用NGINX的expire指令,通过location规定不同文件类型,实现不同的请求转发。expire指令可以设置浏览器缓存过期时间,减少与服务器的请求和流量。当然,在使用CDN内容分发网络的情况下,无需使用此配置。对于动静分离,可参考下文动静分离与缓存部分。
简单使用
nginx简单使用一般为两种情况。流量转发与本地解析。流量转发通常用于将请求流量转发到源站(提供对应服务的服务器)。当然,流量转发也可配置负载均衡。而本地解析用于将访问路径解析到主机指定路径。
注意:所有配置文件在更改后请使用nginx -t
命令以校验配置正确性,并使用systemctl restart nginx
命令重启nginx服务。
配置文件结构
http{
server{
location / {
}
}
}
配置server
server通常按照以下格式进行配置。domian.com为所请求的域名。可以为多级域名。listen为所监听的端口,可以为任意端口。https模式下,证书建议使用lets encrypto。国内厂商可使用亚信,一般腾讯云域名会有免费证书额度,有效期一年。但亚信证书通常都是付费的,价格也不便宜。对于SSL证书管理,可以参考笔者后面发布的文章。
#http模式
server{
listen 80;
server_name domain.com;
}
#https模式
server{
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name domain.com;
ssl_certificate /etc/nginx/cert/domain.crt;
ssl_certificate_key /etc/nginx/cert/domain.key;
}
1.流量转发
以下为简单流量转发配置文件。我们可以使用该配置将流量转发到192.168.0.1服务器的8000都端口。
#该部分放置于server{}内部。
location / {
proxy_pass http://192.168.0.1:8000;
proxy_redirect off;
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
proxy_set_header x-forwarded-host $server_name;
}
2.本地解析
本地解析指将请求解析到本地文件,通常有root,alias两种解析方式。下面我们简述两种解析方式的区别。
root
访问 http://domain.com/url1/file
location ^~ /url1/{
root /home/ubuntu/;
}
此处location解析到服务器的文件路径为: /home/ubuntu/url1/file
alias
访问 http://domain.com/url1/file
location ^~ /url1/{
alias /home/ubuntu/;
}
此处location解析到服务器的文件路径为:/home/ubuntu/file(即alias不会解析location后面配置的路径)
高级使用
nginx鉴权
安装Nginx鉴权工具.
# ubuntu系统
apt install apache2-utils
# centos系统
yum install httpd-tools
生成密码。
此处username为登录用户名,运行此命令后按照步骤输入需要设置的密码即可。
/etc/nginx/.htpasswd为所生成文件保存地址。
htpasswd -c /etc/nginx/.htpasswd username
会话保持
通常,nginx默认会话非持久。未使用upstream的情况下,连接可能会终止。
upstream可以使用于会话保持,也可以使用作负载均衡。
仅会话保持
在nginx.conf内添加以下配置文件。此处为保持与127.0.0.1:8000的会话。此处ip地址可以为公网地址,端口自定义,连接名自定义,此处示例为example。
upstream example {
server 127.0.0.1:8000;
}
在server{}配置内修改location{}内容
location / {
proxy_pass http://example/;
proxy_redirect off;
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
proxy_set_header x-forwarded-host $server_name;
}
负载均衡
负载均衡可以使用以下方案进行线路分配。
- 默认情况下,nginx按照轮询分配请求。
轮询适合服务器配置相当,无状态且短平快的服务使用。另外在轮询中,如果服务器挂掉,会自动剔除该服务器。
upstream example {
server 192.168.0.1:8080;
server 192.168.0.2:8001;
server example.com;
server example.com:8000;
}
- 权重轮询
如果在 upstream 中配置的server参数后追加 weight 配置,则会根据配置的权重进行请求分发。此策略可以与least_conn和ip_hash结合使用,适合服务器的硬件配置差别比较大的情况。
upstream example {
server 192.168.0.1:8080;
server 192.168.0.2:8001;
server example.com;
server example.com:8000;
}
- url_hash
按照url的哈希值分配请求
upstream example {
server 192.168.0.1:8080;
server 192.168.0.2:8001;
server example.com;
server example.com:8000;
url_hash;
}
- fair
fair可以根据页面大小、加载时长智能的进行负载均衡。
upstream example {
server 192.168.0.1:8080;
server 192.168.0.2:8001;
server example.com;
server example.com:8000;
fair;
}
- 最少连接
轮询算法把请求平均转发到各服务器后端,使它们的负载大致相同。但有些请求占用的时长,导致其所在后端负载较高。那么该情况下,least_conn可以达到更好的负载均衡效果,适合请求处理时间长短不一造成服务器过载的情况。least_conn将请求分配给连接数最少的服务器。
upstream example {
server 192.168.0.1:8080;
server 192.168.0.2:8001;
server example.com;
server example.com:8000;
least_conn;
}
- 源IP
该方案nginx将根据源IP的哈希值分配服务器,即仅每个IP拥有对应的服务器。
upstream example {
server 192.168.0.1:8080;
server 192.168.0.2:8001;
server example.com;
server example.com:8000;
ip_hash;
}
备注:
轮询:以循环方式分发对应用服务器的请求,将请求平均分发到每台服务器上。
server后面可使用IP/IP+端口/域名/域名+端口。分号前可标记backup,fail_timeout,max_fails,max_conns,down。
例如 server127.0.0.1 down;
启用websocket
于nginx.conf配置文件的http{}内添加以下内容。
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
于需要启用websocker的location内添加以下内容。
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
访问控制
nginx可自定义访问控制,如限制UA头、限制请求体、限制请求头大小、限制请求源,允许请求源,限制访问文件类型等。下面对于部分限制手段进行举例。
限制ip访问
禁止所有ip访问
deny all;
允许指定ip访问(示例)
allow 192.168.0.1/24;
allow 192.168.1.2;
allow 192.168.2.3;
deny all;
注意,dney all;必须写在最后,否则优先级错误,allow配置不生效。
ip限制可在以下位置写入:
http
: Nginx中所有服务起效server
: 指定的服务起效location
: 满足的location下起效limit_except
: 指定的http方法谓词起效
控制访问路径
云上环境有条件的可以使用云服务厂商的waf,企事业单位可购买安全厂商的waf。
预算不足的可以使用nginx防护
于server{}下禁止访问指定路径,如:
location /upload.php {
return 403;
}
限制请求头
以下示例为限制请求头中包含python字段的请求。^$为不区分大小写。
if ($http_user_agent ~* "python|^$") {
return 403;
break;
}
限制请求体、限制请求头大小。
限制允许客户端请求的最大单文件字节数。示例最大为4G。
client_max_body_size 4G;
限制请求行+请求头的标准大小为1k
client_header_buffer_size 1k;
缓存
缓存就是数据交换的缓冲区(Cache),当用户要获取数据的时候,会先从缓存中去查询获取数据,如果缓存中有就会直接返回给用户,如果缓存中没有,则会发请求从服务器重新查询数据,将数据返回给用户的同时将数据放入缓存,下次用户就会直接从缓存中获取数据。
缓存分为服务端侧和客户端侧。
服务端缓存又分为:代理服务器缓存,反向代理服务器缓存。(也叫网关缓存,比如 Nginx反向代理就可以设置缓存)。
客户端侧缓存一般指的是浏览器缓存、app缓存等等,目的为加速各种静态资源的访问,降低服务器压力。
expires(浏览器侧缓存)
对于expires的定义:对于资源设定过期时间,无需通过服务端验证,仅通过浏览器浏览器自身确认是否过期。当然,与浏览器处清除Cookie和网站数据后重新发起请求,则会重新向服务器请求文件。
expires 指令可以控制 HTTP 应答中的“ Expires ”和“ Cache-Control ”的头标(起到控制页面缓存的作用)
示例(缓存三十天)
location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
{
root /var/www/html/examplt/;
expires 30d;
如上设置后,对于html/htm/gif/jpg/jpeg/bmp/png/ico/txt/js/css文件,客户端请求并缓存后,于三十天后发起请求才会更新缓存数据。而重复发起请求则服务器应当返回304状态码。
304状态码
如果客户端发送了一个带条件的GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个304状态码。
缓存的优点
- 减少数据传输,节省网络流量,加快响应速度,提升用户体验;
- 减轻服务器压力;
- 提供服务端的高可用性;
缓存的缺点
- 数据的不一致;
- 增加成本;
- 降低搜索引擎信任度;
- 关键词排名稳步下降;
- 搜索引擎蜘蛛抓取频率逐渐下降;
因此,该方案会影响SEO(搜索引擎优化),慎用。
服务器侧缓存
NGINX的服务端侧缓存由ngx_http_proxy_module
模块相关指令集完成。
- proxy_cache_path 缓存文件存放路径
- proxy_cache 代理缓存设置
- proxy_cache_key 缓存key值
- proxy_cache_valid 缓存时间
- proxy_cache_min_uses 访问量后缓存
- proxy_cache_methods 指定缓存方法
- upstream_cache_status 缓存命中
PHP内容渲染
默认情况下,直接使用alias或root解析,访问时php文件会因未渲染而无法使用浏览器查看,该情况下php文件会被自动下载。以下为nginx作反向代理的情况下渲染php的方案。
前置步骤
安装php-fpm
apt install php-fpm -y
编辑配置文件并添加设置监听端口。此处监听9002端口。
vim /etc/php/7.4/fpm/pool.d/www.conf
listen 127.0.0.1:9002;
配置完成后,运行以下命令检查配置文件正确性并启动fpm
/usr/sbin/php-fpm7.4 -t && /usr/sbin/php-fpm7.4
使用ps aux | grep php-fpm
命令即可查看php-fpm是否已运行。
以下为nginx配置文件。该配置文件将url请求的路径解析到/home/ubuntu/文件夹下,用户可根据项目位置自行修改。此处fastcgi转发端口对应上文php-fpm所监听的9002端口。
location ~ \.php(.*)$ {
root /home/ubuntu/;
autoindex on;
fastcgi_pass 127.0.0.1:9002;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(![]().+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
php-fpm + nginx 部署方案不建议用于多页面解析部署,fpm在解析时会产生进程并增加使用资源。通常情况下,对于多页面多服务,我们建议使用Docker,使用nginx将流量转发到docker,并于docker内安装php环境。
简单监控
我们可以在server{}内添加以下内容
location /status{
stub_status on;
}
配置以上内容并应用后,可访问server指定的域名/IP/端口加上路径/status查看活跃连接、读、写、等待等信息。
关于status的解释
active connections – 活跃的连接数量
server accepts handled requests — 总共处理了3个连接 , 成功创建3次握手, 总共处理了19个请求
reading — 读取客户端的连接数.
writing — 响应数据到客户端的数量
waiting — 开启 keep-alive 的情况下,这个值等于 active – (reading+writing), 即 Nginx 已经处理完正在等候下一次请求指令的驻留连接。
统一管理与封禁
我们可以通过使用include语句,统一在每个server{}配置文件下新增以下语句。
include conf.d/agent/agent.conf;
在我们管理封禁IP、自定义报错页面、访问UA头等内容,我们只需在、nginx目录下的conf.d/agent/agent.conf文件内更改封禁策略即可实现统一管理。该方案无需运维在每个server{}下管理封禁策略。该方案同样适用于站群。多服务器场景下,我们只需更新agent.conf即可完成所有反向代理的访问控制等变更。同时,该方案不影响针对单个服务的限制,使用该方案的同时可以同时在server{}内写入针对性限制策略。
以下,给出封禁示例配置。
location = /favicon.ico {
alias /var/www/html/favicon.ico;
}
error_page 500 502 503 504 400 403 404 /errors.html;
location = /errors.html {
alias /var/www/html/errors.html;
}
if ($http_user_agent ~* "MJ12bot|Scrapy|python|AhrefsBot|BLEXBot|DotBot|^$") {
return 403;
break;
}
解释:
- location = /favicon.ico
统一管理页面icon,将所有由url请求的icon解析到本地的/var/www/html/favicon.ico文件。 - error_page 500 502 503 504 400 403 404 /errors.html;
统一将所有报错也解析为errors.html - location = /errors.html
指定所有报错页解析到本地的/var/www/html/errors.html文件 - if ($http_user_agent ~* "MJ12bot|Scrapy|python|AhrefsBot|BLEXBot|DotBot|$")\ 封禁请求头中含有MJ12bot、Scrapy、python、AhrefsBot、BLEXBot、DotBot的请求。$为不区分大小写。其中,MJ12bot、Scrapy、AhrefsBot、BLEXBot、DotBot为国外垃圾蜘蛛,seo蜘蛛,营销蜘蛛等,会占用站点大大量资源。经分析,以上蜘蛛每日请求量为百万级。对于python,通常过滤非法爬虫请求。
nginx使用优化
句柄优化
linux/Unix上,一切皆文件,每一次用户发起请求就会生成一个文件句柄,文件句柄可以理解为就是一个索引,所以文件句柄就会随着请求量的增多,而进程调用的频率增加,文件句柄的产生就越多,系统对文件句柄默认的限制是1024个,对Nginx来说非常小了,需要改大一点
根据句柄设置,又可以分为以下三种:
- 系统全局性修改
- 用户局部性修改
- 进程局部性修改
系统全局以及用户局部修改可以通过更下以下文件实现:
vim /etc/security/limits.conf
在End of file前面添加个参数
- soft:软控制,到达设定值后,操作系统不会采取措施,只发提醒
- hard:硬控制,到达设定值后,操作系统会采取机制对当前进程进行限制,这个时候请求就会受到影响
- root:代表root用户(系统全局性修改)
- *:代表全局,即所有用户都受此限制(用户局部性修改)
- nofile:指限制的是文件数的配置项。后面的数字即设定的值,一般设置10000左右
以下为示例配置:
root soft nofile 65535
root hard nofile 65535
* soft nofile 65535
* hasd nofile 65535
局部修改可以通过更改nginx配置文件实现:
vim /etc/nginx/nginx.conf
此处限制每个进程的最大文件打开数,建议与ulimit -n的值保持一致。
worker_rlimit_nofile 35535; #进程限制
CPU关联性设置
该设置视具体情况选择设置,正常使用下不必考虑。
一般设置CPU的核心或者核心数x2为NGINX运行工作进程数。我们可以使用以下命令查看CPU核心数。
/proc/cpuinfo文件 grep ^processor /proc/cpuinfo | wc -l。
4核配置:
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
8核配置:
worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
worker_processes最多为8个,8个以上性能将不会提升,而稳定性也会下降。
NGINX版本1.9后,以下指令可自动绑定CPU。
worker_cpu_affinity auto;
优化
NGINX的连接处理机制在于不同的操作系统会采用不同的I/O模型,Linux下,nginx使用epoll的I/O多路复用模型。
- Linux,使用epoll
- freebsd,使用kqueue
- solaris使用/dev/pool
- windows使用icop
设置work_connections 连接数
worker_connections 10240;
keepalive timeout会话保持时间
keepalive_timeout 60;
GZIP压缩性能优化
gzip on; #表示开启压缩功能
gzip_min_length 1k; #表示允许压缩的页面最小字节数,页面字节数从header头的Content-Length中获取。默认值是0,表示不管页面多大都进行压缩,建议设置成大于1K。如果小于1K可能会越压越大
gzip_buffers 4 32k; #压缩缓存区大小
gzip_http_version 1.1; #压缩版本
gzip_comp_level 6; #压缩比率, 一般选择4-6,为了性能gzip_types text/css text/xml application/javascript; #指定压缩的类型 gzip_vary on; #vary header支持
proxy超时设置
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k
高效传输模式
sendfile on; # 开启高效文件传输模式。
tcp_nopush on; #需要在sendfile开启模式才有效,防止网路阻塞,积极的减少网络报文段的数量。将响应头和正文的开始部分一起发送,而不一个接一个的发送。
Nginx尽可能达到更好的性能,我们还需要调整在服务器上的内核参数。
vim /etc/sysctl.conf
更高后执行sysctl -p
载入配置文件,使配置生效
1)调节系统同时发起的tcp连接数
net.core.somaxconn = 262144
2)允许等待中的监听
net.core.somaxconn = 4096
3) tcp连接重用
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
4)不抵御洪水攻击
net.ipv4.tcp_syncookies = 0
net.ipv4.tcp_max_orphans = 262144 #该参数用于设定系统中最多允许存在多少TCP套接字不被关联到任何一个用户文件句柄上,主要目的为防止Ddos攻击
5)最大文件打开数
在命令行中输入如下命令,即可设置Linux最大文件打开数。
ulimit -n 30000
相关命令:
查看cpu核心数
cat /proc/cpuinfo|grep "cpu cores"|uniq
显示物理cpu数量:
cat /proc/cpuinfo | grep "physical id"|sort|uniq|wc -l
查看cpu使用率
top 回车后按 1
查看nginx使用cpu核心和对应的nginx进程号
ps -eo pid,args,psr | grep [n]ginx
本文来自博客园,作者:K龙,转载请注明原文链接:https://www.cnblogs.com/kaydenlsr/p/16717768.html