Nginx调优
一、性能优化考虑点
当我们需要进行性能优化时,通常意味着现有的服务器无法满足不断增长的业务需求。性能优化是一个涉及多方面的课题,需要从以下几个方面进行探讨。
1、确定当前系统结构瓶颈
在进行性能优化之前,首先要找出现有系统中的瓶颈。这可能包括 CPU、内存、磁盘 I/O、网络等方面的限制。通过监控工具和性能分析,可以帮助我们了解瓶颈所在,并有针对性地进行优化。
2、了解业务模式
深入了解业务需求和模式,以便更好地为其量身定制优化方案。例如,根据业务请求的特点,可以选择适当的负载均衡策略、缓存策略和数据存储方案。
3、原理、性能和安全的平衡
在进行性能优化时,需要平衡原理、性能和安全之间的关系。过度追求性能可能导致系统的可维护性降低,甚至影响安全性。因此,优化过程中要充分考虑这三者之间的平衡,以实现最佳的性能提升。
在进行性能优化时,我们可以从系统架构、软件配置、硬件资源等多个层面进行改进。通过全面分析现有系统的状况,我们可以制定出更加合适的优化策略,提升系统的性能,满足业务的发展需求。
二、master-worker机制
master-worker工作原理图
一个 master (进程) 管理多个 worker (进程)
master-worker机制
争抢机制示意图
1、Nginx 采用 Master-Worker 架构,即一个 Master 进程管理多个 Worker 进程(多进程结构),而非多线程结构。
2、当client发出请求时,Master 进程会通知其管理的 Worker 进程。
3、Worker 进程开始抢占任务,成功抢占的 Worker 进程会建立连接并完成任务。
4、每个 Worker 都是独立的进程,且在每个进程内只有一个主线程。
5、Nginx 使用了 I/O 多路复用机制(需要在 Linux 环境下),这使得 Nginx 能够在有限的 Worker 进程下实现高并发。
通过这种 Master-Worker 机制,Nginx 能够充分利用系统资源,实现高性能、高并发的服务。
master-worker机制示意图
master-worker模式
1、Nginx 在启动后会存在一个 Master 进程和多个相互独立的 Worker 进程。
2、Master 进程负责接收外界的信号,并向各 Worker 进程发送信号。每个 Worker 进程都有可能处理这些连接。
3、进程能监控 Worker 进程的运行状态。当 Worker 进程异常退出时,Master 进程会自动启动新的 Worker 进程以保持服务的稳定性。
通过这种 Master-Worker 模式,Nginx 能够实现更高的可靠性和稳定性,同时有效地处理大量并发连接。
accept_mutex解决“惊群现象”
1、惊群现象是指当一个新连接到来时,多个子进程都从继承的父进程 sockfd 收到通知并竞相与新连接建立连接的现象。
2、在惊群现象中,大量进程被激活又挂起,只有一个进程能实际 accept() 到这个连接,这会消耗系统资源。
3、为解决惊群现象,Nginx 提供了 accept_mutex,这是一个加在 accept 上的共享锁。在执行 accept 之前,每个 Worker 进程需要先获取锁,获取不到则放弃执行 accept()。这样,在同一时刻,只会有一个进程去 accept(),从而避免惊群现象。
4、当一个 Worker 进程 accept() 到连接后,它会开始读取请求、解析请求、处理请求、产生响应数据,最后返回给客户端并断开连接,完成一个完整的请求处理。
5、一个请求完全由一个 Worker 进程处理,且只能在一个 Worker 进程中处理。
通过使用 accept_mutex,Nginx 能够有效地解决惊群现象,降低系统资源消耗,提高服务性能。
用多进程结构而不用多线程结构的好处
1、节省锁开销:每个 Worker 进程都是独立的进程,它们不共享资源,因此不需要加锁。这降低了系统开销,并在编程和问题调试方面提供了便利。
2、独立进程降低风险:采用独立的进程可以确保互相之间不会受到影响。如果一个进程出现问题并退出,其他进程仍然可以继续工作,服务不会中断。同时,Master 进程会快速启动新的 Worker 进程以维护服务稳定性。
实现高并发的秘密-IO多路复用
1、尽管 Nginx 的每个进程只有一个主线程,但它依然能实现高并发。这得益于 IO 多路复用的原理,通过异步非阻塞的事件处理机制和 epoll 模型,实现了轻量级和高并发。
2、具体来说,当一个请求进入时,会有一个 Worker 进程处理。然而,Worker 进程并不会全程处理请求,而是处理到可能发生阻塞的地方。例如,Worker 进程向上游(后端)服务器转发请求并等待响应。
3、此时,Worker 进程不会闲等,而是在发送完请求后注册一个事件:“如果上游服务器返回数据,通知我继续处理。”然后,Worker 进程开始处理其他请求。
4、当上游服务器返回数据时,触发了之前注册的事件,Worker 进程重新接手该请求并继续处理。
由于 Web 服务器的工作性质使得每个请求的大部分时间都花费在网络传输中,而在服务器上的实际处理时间较短。这就是为什么少量进程就能解决高并发的秘密所在。
小结:Nginx的master-worker工作机制的优势
1、支持热部署:可以通过 nginx -s reload 实现配置的热更新,无需重启服务。
2、独立进程减少锁开销:每个 Worker 进程独立运行,无需加锁。这降低了系统开销,并在编程和问题调试方面提供了便利。
3、异步非阻塞 / IO 多路复用:尽管每个进程只有一个主线程,但通过异步非阻塞的方式和 IO 多路复用来处理请求,实现高并发。
4、进程间互不影响,提高稳定性:独立进程之间不会相互影响,即使一个 Worker 进程退出,其他 Worker 进程仍能工作,服务不会中断。Master 进程会快速启动新的 Worker 进程以维护服务稳定性。
5、充分利用 CPU 性能:将一个 Worker 分配给一个 CPU,使得 Worker 线程能够充分发挥 CPU 性能。
三、系统与Nginx性能调优
在了解了系统瓶颈和现状之后,我们可以针对系统性能进行全面优化。
网络:网络流量、丢包情况以及网络稳定性都会影响用户请求。
系统:系统负载、饱和度、内存使用率、系统稳定性以及硬盘磁盘损坏情况都需要关注。
服务:通过在 Nginx 中根据业务需求进行设置,可以进行连接优化、内核性能优化以及 HTTP 服务请求优化。
程序:需要关注接口性能、请求处理速度以及每个程序的执行效率。
数据库和底层服务:考虑数据库性能和底层服务对整体性能的影响。
1、文件句柄
在 Linux/Unix 系统中,一切皆文件。每次用户发起请求时,系统会生成一个文件句柄。文件句柄可以看作是文件的索引,随着请求量的增加,进程对文件句柄的调用频率也会相应提高,导致文件句柄数量增多。系统对文件句柄的默认限制是 1024 个,但对于 Nginx 来说,这个限制过小,需要适当调整以提高性能。
ulimit -n #linux 下可以查看系统设置的最大文件句柄
(1)设置方式
系统全局性修改
用户局部性修改
进程局部性修改
(2)系统全局性修改和用户局部性修改
vim /etc/security/limits.conf
soft:软控制,到达设定值后,操作系统不会采取措施,只是发提醒
hard:硬控制,到达设定值后,操作系统会采取机制对当前进行进行限制,这个时候请求就会受到影响
root:这里代表root用户(用户局部性修改)
*:代表全局,即所有用户都受此限制(系统全局性修改)
nofile:指限制的是文件数的配置项。后面的数字即设定的值,一般设置10000左右
尤其在企业新装的系统,这个地方应该根据实际情况进行设置,可以设置全局的,也可以设置用户级别的。
su #刷新以下环境
ulimit -n #再次查看系统最大文件句柄
这里就改为了,我们对root用户设置的最大文件句柄65535
(3)进程局部性修改
vim /usr/local/nginx/conf/nginx.conf
每个进程的最大文件打开数,所以最好与ulimit -n的值保持一致。实际上,Nginx 工作进程文件句柄限制受系统文件句柄限制的约束。这意味着,Nginx 工作进程文件句柄限制的值不能超过系统文件句柄限制的值。
重启一下服务
2、cpu的亲和配置&worker数量调优
CPU 亲和性配置使得 Nginx 可以将不同的 worker 进程绑定到不同的 CPU 核心上。这样做可以减少 worker 之间在不同 CPU 核心之间频繁切换的次数,降低进程迁移的频率,从而减少性能损耗。
为了充分利用服务器性能,建议将 worker 数量设置为等于服务器 CPU 核心数。这样可以确保每个 worker 线程都能充分利用单个 CPU 的性能。如果 worker 数量设置过少,可能会导致 CPU 资源浪费;而设置过多,则会引起 CPU 频繁切换上下文,进而产生额外的性能损耗。
(1)cpu亲和性配置
Nginx允许工作进程个数一般设置CPU的核心或者核心数*2.如果不了解cpu核数,可以使用top后按1看出来,也可以查看/proc/cpuinfo文件
cat /proc/cpuinfo | grep ^processor | wc -l #查看cpu核数
lscpu | grep "CPU(s)" #另一种查看cpu核数的方式
这里我是4核
随后进入nginx配置文件进行修改。需要知道的就是nginx 1.9版本之后,就帮我们自动绑定了cpu;
所以我们只需要打开cpu亲和就行
vim /usr/local/nginx/conf/nginx.conf
user nginx;
worker_processes 4;
worker_cpu_affinity auto;
#或者 worker_cpu_affinity 0001 0010 0100 1000;
worker_rlimit_nofile 65535;
worker_rlimit_nofile 参数用于更改 Nginx worker 进程的最大打开文件数限制。默认情况下,该值取决于操作系统的限制。为了避免 “too many open files” 问题并提高 Nginx 的性能,可以适当提高此值。但在调整 worker_rlimit_nofile 值之前,请确保先调整操作系统的文件句柄限制,以允许 Nginx 使用更多的文件句柄。
需要注意的是,过高的 worker_rlimit_nofile 值可能导致系统资源耗尽,因此建议根据实际需求和服务器资源情况合理设置这个值。
重启一下nginx,查看一下nginx worker进程绑定对应的cpu
ps -eo pid,args,psr | grep -v grep | grep nginx
这样就能看到nginx的子进程都绑定到了哪些cpu核上
设置worker数量,Nginx 默认没有开启利用多核cpu,可以通过增加worker_cpu_affinity配置参数来充分利用多核cpu的性能
#2 核 cpu,开启 2 个进程
worker_processes 2;
worker_cpu_affinity 01 10;
#2 核 cpu,开启 4 个进程,
worker_processes 4;
worker_cpu_affinity 01 10 01 10;
#4 核 cpu,开启 2 个进程,0101 表示开启第一个和第三个内核,1010 表示开启第二个和第四个内核;
worker_processes 2;
worker_cpu_affinity 0101 1010;
#4 个 cpu,开启 4 个进程
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
#8 核 cpu,开启 8 个进程
worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
worker_cpu_affinity 理解
(2)worker数量调优
如果不确定如何设置 Nginx 中的 worker_processes 参数,将其设置为与可用 CPU 核心数相等是一个很好的选择。这样,每个 CPU 内核都可以运行一个 worker 进程,从而最有效地利用硬件资源。
官方的说明如下
# 推荐方式:
worker_processes auto;
这将自动设置 worker_processes 为与可用 CPU 核心数相等。
# 可选方式:
# 假设 VCPU(可用 CPU 核心数)为 4
# 可以将 worker_processes 设置为 VCPU - 1
# 查看 VCPU 数量的命令:
grep "processor" /proc/cpuinfo | wc -l worker_processes 3;
请注意,上述可选方式中的 “VCPU - 1” 可能不适用于所有场景,建议根据实际需求和服务器资源情况进行调整。
3、事件处理模型&连接处理multi_accept优化
Nginx 的连接处理机制根据不同的操作系统采用不同的 I/O 模型。在 Linux 系统下,Nginx 使用 epoll 作为 I/O 多路复用模型;在 FreeBSD(类 Unix 操作系统)下,使用 kqueue 作为 I/O 多路复用模型;在 Solaris(Unix 系统的一个重要分支操作系统)下,使用 /dev/poll 方式的 I/O 多路复用模型;在 Windows 下,使用 IOCP 等。为了获得最佳性能,建议根据操作系统类型选择适当的事件处理模型。如果你使用的是 CentOS(一种 Linux 发行版),那么我推荐将 Nginx 的事件处理模型调整为 epoll。
进入nginx配置文件再进行修改
events {
worker_connections 10240;
multi_accept on;
use epoll;
}
multi_accept 参数告诉 Nginx 在收到一个新连接通知后尽可能多地接受连接。默认值是 on。当设置为 on 时,多个 worker 会按串行方式处理连接,即一个连接只唤醒一个 worker,其他 worker 处于休眠状态。当设置为 off 时,多个 worker 会按并行方式处理连接,即一个连接会唤醒所有 worker,直到连接分配完毕,未获得连接的 worker 继续休眠。
当服务器连接数较少时,开启 multi_accept 参数(设置为 on)有助于降低负载。然而,当服务器吞吐量较大时,为了提高效率,可以考虑关闭此参数(设置为 off)。根据实际需求和服务器资源情况进行调整。
4、设置work_connections连接数
worker_connections 参数表示每个 worker(子进程)可以创建的最大连接数。
默认值:worker_connections: 1024
最大值:worker_connections: 65535
需要根据系统的最大打开文件数来调整此参数。
系统的最大打开文件数应大于等于 worker_connections * worker_processes。
为了确保正常运行,worker_connections 参数应小于等于系统的最大打开文件数。
实际的 worker_connections 连接数为:worker_connections * worker_processes
要查看linux系统的最大打开文件数,可以运行以下命令:
ulimit -a | grep "open files"
你可以根据服务器性能和需求调整 worker_connections。在本例中,我们将其设置为 10240:
worker_connections 10240; #nginx设置
根据最大连接数计算最大并发数
对于支持 HTTP 1.1 的浏览器,每次访问通常占用两个连接。因此,对于普通的静态访问,最大并发数为:worker_connections * worker_processes / 2
对于 Nginx 作为反向代理的情况,最大并发数量应为:worker_connections * worker_processes / 4。这是因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,分别占用两个连接。
看一个示意图
5、keepalive timeout会话保持时间
keepalive_timeout 65;
keepalive_timeout参数设置长连接的会话保持时间。这个值表示浏览器与服务器之间的连接在多久内没有活动后会被关闭。在此期间,如果有新的请求产生,计时器将会被重置。因此,这个值不应设置得过长,通常 60 秒左右即可。
此外,还有一些与 keepalive 相关的配置参数:
keepalive:指定向上游服务器保留的连接数。
keepalive_disable:禁用某些浏览器的 keepalive 功能。
keepalive_requests:设置在一个长连接中可以并发接收的请求数量。默认值为 1000。
send_timeout:在向客户端发送数据时,如果在建立连接后两次活动时间内服务器没有返回数据,那么连接将被关闭。
keepalive_time:设置 TCP 连接的总时长。超过此时间,将需要重新建立连接。
总之,keepalive 是一种长连接机制,通过 keepalive_timeout 参数设置会话保持时间,可以有效地减少不必要的连接建立和关闭过程,从而提高服务器性能。
6、GZIP压缩性能优化
gzip on; # 开启gzip压缩功能,减少传输数据量,提高网页加载速度。
gzip_min_length 1k; # 设置允许压缩的页面最小字节数为1KB,避免压缩过小的文件导致效果适得其反。
gzip_buffers 4 32k; # 设置压缩缓冲区大小,分配4个32KB的内存用于存储gzip压缩结果,提高压缩性能。
gzip_http_version 1.1; # 设置识别HTTP协议版本为1.1,适应大部分浏览器对GZIP解压的支持。
gzip_comp_level 6; # 设置gzip压缩比例为6,平衡压缩速度和压缩效果,减轻CPU负担。
gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript text/css application/xml; # 指定需要压缩的文件类型,提高压缩效果,默认值是 gzip_types text/html 。
gzip_vary on; # 启用vary header支持,允许前端缓存服务器(如Squid)缓存经过gzip压缩的页面,提高缓存效率。
7、连接超时时间及其他优化
以下配置参数用于优化连接超时时间和提高服务器性能,同时保护服务器资源(如 CPU 和内存)。
keepalive_timeout 60; # 设置长连接超时时间为60秒,保持客户端和服务器之间的连接,提高性能。
tcp_nodelay on; # 启用TCP_NODELAY选项,降低延迟,提高响应速度。
client_header_buffer_size 4k; # 设置客户端请求头缓冲区大小为4KB,确保请求头可以被正确处理。
open_file_cache max=102400 inactive=20s; # 缓存打开文件的描述符,加速文件访问,减轻磁盘I/O压力。
open_file_cache_valid 30s; # 确保文件的有效性和一致性,缓存的文件在30秒后被认为是无效的。
open_file_cache_min_uses 1; # 提高缓存的使用率,避免不必要的缓存更新,在文件被认为无效之前,需要至少使用1次。
client_header_timeout 15; # 设置读取客户端请求头的超时时间为15秒,避免长时间等待恶意或缓慢的请求,提高服务性能。
client_body_timeout 15; # 设置读取客户端请求体的超时时间为15秒,避免长时间等待恶意或缓慢的请求,提高服务性能。
reset_timedout_connection on; # 启用超时连接重置,当连接超时时,Nginx将关闭连接并释放资源,提高服务的可用性。
send_timeout 15; # 设置发送响应数据给客户端的超时时间为15秒,避免因客户端无法接收数据而导致的服务阻塞,提高性能。
server_tokens off; # 禁止Nginx在响应头中显示版本信息,提高安全性,防止攻击者利用已知的版本漏洞攻击。
client_max_body_size 10m; # 设置客户端请求主体的最大大小为10MB,限制上传文件大小,保护服务器免受大文件上传导致的资源消耗。
8、代理超时设置(proxy)
以下配置参数用于设置 Nginx 代理服务器的超时时间和缓冲区大小:
proxy_connect_timeout 90; # 后端服务器连接的超时时间,发起握手等候响应超时时间。
proxy_send_timeout 90; # 后端服务器数据回传时间,即规定时间内后端服务器必须传完所有数据。
proxy_read_timeout 90; # 连接成功后,等待后端服务器响应时间。此时间内后端服务器应处理请求并回应(实际上是进入后端服务器排队等待处理)。
proxy_buffers 4 32k; # 设置缓存区的数量(4 个)和大小(32 KB)。从被代理的后端服务器获取的第一部分响应内容会放置到这里。默认情况下,一个缓存区的大小等于内存页面大小(通常为 4 KB 或 8 KB,取决于平台)。
proxy_busy_buffers_size 64k; # 设置 Nginx 向客户端发送数据时所使用的缓冲区大小。通常为 proxy_buffers 单位大小的两倍。官方默认值为 8 KB 或 16 KB。
关于缓冲区buffer的工作原理,请注意以下几点:
所有的 proxy buffer 参数都是针对每个请求生效的。每个请求会根据配置参数获得自己的 buffer。proxy buffer 不是全局配置,而是每个请求的配置。
proxy_buffering 用于开启反向代理服务器响应数据的缓存。开启后,proxy_buffers 和 proxy_busy_buffers_size 参数才会生效。
无论 proxy_buffering 是否开启,proxy_buffer_size 都会生效。proxy_buffer_size 设置的缓冲区大小用于存储上游服务器响应的 header。
在 proxy_buffering 开启的情况下,Nginx 会尽可能地将上游服务器传输的所有数据读取到 buffer,直到 proxy_buffers 设置的所有 buffer 被写满或数据被完全读取。此时,Nginx 开始向客户端传输数据,会同时传输整个缓冲区序列。如果响应内容较大,Nginx 会接收并将其写入临时文件,大小由 proxy_max_temp_file_size 控制。当繁忙的缓冲区传输完毕后,Nginx 会继续从临时文件中读取数据,直到传输完成。
一旦 proxy_buffers 设置的 buffer 被写入,直到缓冲区内的数据完全传输完毕(传输到客户端),该缓冲区将一直处于繁忙状态,我们不能对该缓冲区执行任何其他操作
9、proxy_set_header
proxy_set_header 用于设置被代理服务器接收到的 header 信息。
语法:proxy_set_header field value;
field:需要更改的 header 字段,例如 “Host”
value:header 字段的值
如果不设置 proxy_set_header,则 “Host” 默认值为 proxy_pass 后面的域名或 IP(通常使用 IP)。
proxy_set_header X-Real-IP $remote_addr; 和 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 用于设置被代理端接收到的远程客户端 IP。如果不设置这两个参数,则 header 信息中不会透传远程真实客户端的 IP 地址。
server {
...
location /http/ {
proxy_pass http://http_backend; # 代理转发到后端服务器
proxy_http_version 1.1; # 使用 HTTP/1.1 协议
proxy_set_header Connection ""; # 清除 "Connection" 头字段,避免 "Connection: keep-alive"
proxy_set_header Host $http_host; # 添加 "Host" 头字段,使用原始请求的域名
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 将客户端和代理链路的 IP 添加到 "X-Forwarded-For" 头字段
proxy_next_upstream error timeout http_500 http_502 http_503 http_504; # 当后端服务器返回指定错误码时,请求被转发到下一个服务器
proxy_connect_timeout 60s; # 代理与后端服务器连接超时时间
proxy_read_timeout 60s; # 代理等待后端服务器响应时间
proxy_send_timeout 60s; # 后端服务器将数据发送回代理的超时时间
}
...
}
关于变量的说明:
$proxy_host:代表 proxy_pass 后面跟着的 host(域名或 IP)
$http_host:始终等于 HTTP 请求头中的 “Host” 字段
$host:等于 $http_host,但是小写且没有端口号(如果存在)
$proxy_add_x_forwarded_for:从客户端到后端服务经过的所有节点的 IP 地址汇总
$remote_addr:代表客户端的 IP 地址,由服务器根据客户端的 IP 指定
$remote_addr 只能获取到与服务器直连的上层请求 IP,但当通过 CDN 或其他代理访问时,后端服务器获取到的将是 CDN 或代理的 IP,而非真实用户 IP。此时,使用 “X-Forwarded-For” 可以记录从客户端真实 IP 到所有经过的代理节点的 IP,以获取用户真实 IP。设置 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 可以获取所有的代理 IP 和客户端 IP。
10、高效传输模式
http {
...
# 高效传输模式设置
sendfile on; # 开启高效文件传输模式,使用操作系统提供的 sendfile() 函数进行零拷贝文件传输
tcp_nopush on; # 在 sendfile 模式下生效,将响应头和正文的开始部分一起发送,减少网络报文段的数量,防止网络阻塞
tcp_nodelay on; # 不论数据包大小,只要有数据包产生,就尽快传输,降低传输时延
...
}
11、fastcgi调优
http {
...
# FastCGI 调优配置
fastcgi_connect_timeout 600; # 连接到后端 FastCGI 服务器的超时时间
fastcgi_send_timeout 600; # 向 FastCGI 服务器发送请求的超时时间
fastcgi_read_timeout 600; # 接收 FastCGI 服务器响应的超时时间
fastcgi_buffer_size 64k; # 读取 FastCGI 响应的第一部分所需的缓冲区大小
fastcgi_buffers 4 64k; # 缓冲 FastCGI 响应所需的缓冲区数量和大小
fastcgi_busy_buffers_size 128k; # 繁忙时的缓冲区大小,建议设置为 fastcgi_buffers 的两倍
fastcgi_temp_file_write_size 128k; # 将数据块写入 fastcgi_temp_path 时的大小,默认值是 fastcgi_buffers 的两倍
fastcgi_temp_path /usr/local/nginx1.22/nginx_tmp; # 缓存临时目录
fastcgi_intercept_errors on; # 是否传递 4xx 和 5xx 错误信息到客户端,或让 Nginx 使用 error_page 处理错误信息
fastcgi_cache_path /usr/local/nginx1.22/fastcgi_cache levels=1:2 keys_zone=cache_fastcgi:128m inactive=1d max_size=10g; # FastCGI 缓存目录、缓存空间名称、内存大小、失效时间和硬盘空间大小
...
server {
...
location ~ \.php$ {
...
fastcgi_pass 127.0.0.1:9000; # 指定 FastCGI 服务器监听端口与地址
fastcgi_cache cache_fastcgi; # 开启 FastCGI 缓存并为其指定一个名称
fastcgi_cache_valid 200 302 1h; # 将 200 和 302 应答缓存一小时
fastcgi_cache_valid 301 1d; # 将 301 应答缓存一天
fastcgi_cache_valid any 1m; # 将其他应答缓存为 1 分钟
fastcgi_cache_min_uses 1; # 相同 URL 请求多少次后将被缓存
fastcgi_cache_key "http://$host$request_uri"; # 设置 Web 缓存的 Key 值,根据 host 和 request_uri 组合而成
...
}
...
}
...
}
总结:
Nginx 的缓存功能主要包括:proxy_cache 和 fastcgi_cache。
proxy_cache 的作用是缓存后端服务器的内容,可能包括静态和动态内容。通过 proxy_cache 缓存,Nginx 可以减少与后端服务器之间的通信次数,节省传输时间和后端服务器的带宽。(12、proxy_cache会提到)
fastcgi_cache 的作用是缓存 FastCGI 生成的内容,通常用于缓存 PHP 生成的动态内容。通过 fastcgi_cache 缓存,Nginx 可以减少与 PHP 之间的通信次数,进一步减轻 PHP 和数据库(例如 MySQL)的压力。
总之,通过这两种缓存机制,Nginx 可以提高网站的响应速度,减轻服务器负载,并降低服务器资源消耗。
12、proxy_cache缓存
http {
# 定义代理缓存路径和参数
proxy_cache_path /path/to/cache levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
listen 80;
server_name example.com;
location / {
proxy_cache my_cache; # 启用代理缓存
proxy_cache_valid 200 302 60m; # 设置缓存有效期:200 和 302 状态码的响应为 60 分钟
proxy_cache_valid 404 1m; # 设置缓存有效期:404 状态码的响应为 1 分钟
proxy_cache_key "$scheme$request_method$host$request_uri"; # 设置缓存键
# 当缓存内容过期时,在更新缓存之前仍然使用旧的缓存内容
proxy_cache_use_stale updating error timeout http_500 http_502 http_503 http_504;
proxy_pass http://your-backend-server; # 设置代理服务器
# 设置请求头信息
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_http_version 1.1; # 使用 HTTP/1.1 协议
proxy_set_header Connection ""; # 清除 "Connection" 头字段,避免 "Connection: keep-alive"
proxy_buffering on; # 使用缓冲区,代理将后端返回的内容逐步发送给客户端
proxy_buffer_size 4k; # 设置代理保存用户头信息的缓存区大小
proxy_buffers 8 8k; # 设置代理缓冲区大小 (8 * 8 = 64k)
}
}
}
补充知识:
除了 Nginx 代理缓存和浏览器缓存外,还有一些其他类型的缓存,例如:
CDN 缓存:内容分发网络(CDN)可以缓存静态资源,将其分发到全球各地的边缘节点,以加速资源加载。CDN 缓存的配置取决于您选择的 CDN 提供商。
内存缓存:某些应用程序框架和数据库提供了内存缓存机制,如 Memcached 和 Redis。这些缓存通常用于存储数据库查询结果、会话数据等,以减少数据库负担并提高应用性能。内存缓存的配置需要在应用程序代码或数据库层面进行。
全页缓存:某些内容管理系统(如 WordPress)支持全页缓存插件,可以将整个 HTML 页面缓存在服务器上,提高页面加载速度。这类缓存的配置通常在 CMS 中进行。
13、expires缓存调优
缓存主要针对于图片、CSS、JS 等更改较少的元素,特别是图片。由于图片占用较大的带宽,我们可以设置图片在浏览器本地缓存 365 天,而 CSS、JS 和 HTML 可以缓存 10 天左右。这样,虽然用户第一次访问网站时加载速度较慢,但从第二次开始,加载速度将会明显加快。在配置缓存时,我们需要列出需要缓存的扩展名。Expires 缓存配置在 server 块内。
location ~* \.(ico|jpe?g|gif|png|jpeg|bmp|swf|flv)$
{
expires 365d; # 过期时间为 365 天
#log_not_found off;
access_log off;
}
location ~* \.(js|css)$
{
expires 10d;
log_not_found off;
access_log off;
}
注:log_not_found off; 表示是否在 error_log 中记录不存在的错误,默认为记录。
Expires 是缓存到期的时间,这里指的是缓存到客户端的浏览器上的缓存。通过这种方式可以减轻服务器的负担,并减少网络传输,提高用户访问网站的速度。
14、访问限流(rate limiting)
我们构建网站的目的是让用户合法地访问它们。因此,必须采取一些措施来限制滥用访问的用户。滥用访问通常是指同一 IP 每秒向服务器发送大量请求。这可能是在同一时间内,来自世界各地多台机器上的爬虫机器人多次尝试爬取网站内容。
限速方法
限速可以通过多种方式实现:
1)下载速度限制
2)单位时间内请求数限制
3)基于客户端的并发连接限制
nginx限速模块
Nginx 官方版本提供了两个模块来分别限制 IP 的连接和并发:
limit_req_zone 用于限制单位时间内的请求数,即速率限制。采用漏桶算法(Leaky Bucket)。
limit_conn 用于限制同一时间的连接数,即并发限制。
应用场景
下载限速:限制下载速度和并发连接数,应用于下载服务器中,以保护带宽和服务器的 I/O 资源。
请求限速:限制单位时间内用户的访问请求,防止恶意攻击,保护服务器及资源安全。
限速原理
漏桶算法(Leaky Bucket)
漏桶算法的核心思想包括以下几点:
请求(水)从上方倒入水桶,从水桶下方流出(被处理)。
未能及时流出的请求(水)暂存在水桶中(缓冲),以固定速率流出。
当水桶满时,多余的请求(水)会溢出(丢弃)。
漏桶算法的核心在于缓存请求、匀速处理以及丢弃多余的请求。
令牌桶算法(Token Bucket)(上右图)
令牌桶算法与漏桶算法的不同之处在于:
令牌桶算法有一个令牌桶,用于存放令牌。
令牌桶算法还有一个队列,用于存放请求。
令牌桶算法的工作原理是,请求在获得足够的令牌后才能被处理。令牌以固定速率生成,因此令牌桶算法实现了对请求的限速处理。此外,令牌桶算法允许一定程度的突发流量,因为令牌是积累在桶中的,如果有足够的令牌,多个请求可以同时得到处理。
简要补充其他算法:
滑动窗口算法(Sliding Window):滑动窗口算法通过在一段时间窗口内统计请求量来实现限速。窗口在时间轴上滑动,当窗口内的请求量达到限制值时,新的请求会被拒绝或延迟处理。滑动窗口算法可以更灵活地处理突发流量。
漏桶 + 令牌桶组合:这种算法结合了漏桶和令牌桶的特点,通常用于处理具有复杂限速需求的场景。例如,可以在令牌桶的基础上增加一个漏桶,以实现更精细化的限速策略。
限速实现
1)单位时间内请求数限制
DDoS攻击者会发送大量的并发连接,占用服务器资源(包括连接数、带宽等),这会导致正常用户处于等待状态或无法访问服务器。
Nginx 提供了一个 ngx_http_limit_req_module 模块,可以有效降低 DDoS 攻击的风险,防止 DDoS 攻击,或防止上游服务器同时被太多请求淹没。
16,000 个 IP 地址的状态信息大约需要 1MB。
以下是基于 IP 对下载速率进行限制的示例,限制每秒处理 1 次请求,对突发超过 5 个以后的请求放入缓存区:
http {
limit_req_zone $binary_remote_addr zone=baism:1m rate=1r/s;
server {
location /abc {
limit_req zone=baism burst=5 nodelay;
}
}
}
参数解释:
limit_req_zone $binary_remote_addr zone=baism:10m rate=1r/s;
第一个参数:$binary_remote_addr ,表示通过 remote_addr 这个标识来做限制,binary_ 的目的是减小内存占用量,限制同一客户端 IP 地址。$binary_remote_addr 为 IPv4 时占用 4B,为 IPv6 时占用 16B。
第二个参数:zone=baism:10m,表示生成一个大小为 10M,名字为 baism 的内存区域,用来存储访问的频次信息。
第三个参数:rate=1r/s,表示允许相同标识的客户端的访问频次,这里限制的是每秒 1 次,还可以有例如 30r/m 的,即每 2 秒才能处理一个请求。
limit_req zone=baism burst=5 nodelay;
第一个参数:zone=baism ,设置使用哪个配置区域来做限制,与上面 limit_req_zone 里的 name 对应。
第二个参数:burst=5,这个配置的意思是设置一个大小为 5 的缓冲区,当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,但是这个等待区里的位置只有 5 个, 超过的请求会直接报 503 的错误然后返回。
第三个参数:nodelay,如果设置,会在瞬时提供处理 (burst+rate) 个请求的能力,请求超过 (burst+rate) 的时候就会直接返回 503, 永远不存在请求需要等待的情况(这里的 rate 的单位是:r/s)。如果没有设置,则所有请求会依次等待排队。
burst详解及实验:
设定一个场景,一个 Nginx 反向代理的后端服务单页请求数是 30 个。如果我们设置 rate 为 30r/s,那么这个 burst 设置就没有意义了。但是,如果我们不想把 rate 设置得这么大,比如 5r/s,同时还想完成一个页面 30 个请求的需求。这个时候 burst 就发挥作用了(前后端一体化的服务,其静态资源单次刷新后,再访问时只会刷新动态请求)。
假设我们一个页面有 30 个请求,后面每秒都会刷新一个页面,但是新页面由于静态资源已经刷新,只有 2-3 个请求。因此,我们把 burst 设置成 200 个(可以让用户连续强制刷新大约 8 次)。
当我们第一次访问时,由于 rate 是 5 个,只能处理 5 个,另外的 25 个会放到 burst 队列。虽然设置成 nodelay 会把这 25 个也处理了,但是队列中占用的 25 个位置需要 25/rate=5 秒来清除。如果第二次请求是 3 个,那么我们实时处理了 3 个请求,并且 rate 剩余 2 个可以清空 burst 队列,这个时候,队列就是占用了 23 个位置。最后,如果队列满了,那么就会出现 503 的请求。
验证效果:基本按上面的方式,连续每秒 30 个请求,即 F5 强制刷新,那么大约会在多少次后出现 503 的情况呢?答案大约是 8 次(200/25)。
burst = (每次请求数 - 请求限制数) * 允许强制刷新的次数
例子演示:
首先我们配置了limit_req_zone,rate=10r/m,即每六秒才处理一次请求,如下:
1.首先测试不加burst和不加nodelay的情况:
查看当前的tcp连接数
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
结果如下:
使用ab测试工具,发起10个并发请求:
ab -n 10 -c 10 url #linux 下测试
ab工具返回结果:
可以看到一共10个请求,9个请求都失败了。且0.002秒就完成了压测
接着查看当前的tcp连接数:
可以观察到此时服务端的TIME_WAIT 比上图多了10,这意味着是服务端主动要求断开了所有TCP连接
接着再查看 /var/log/nginx/access.log,印证了只有一个请求成功了,其它就是都直接返回了503,即服务器拒绝了请求。
2. 只加burst和不加nodelay的情况:
查看当前的tcp连接数
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
结果如下:
使用ab测试工具,发起10个并发请求:
ab -n 10 -c 10 url #linux测试
可以看到测试经过30s才结束
ab工具返回结果:
压测中一共10个请求,有4个请求失败了,直接返回503
查看当前的tcp连接数:
上图是ab测试第一秒时的截图,对比第一次截图,TIME_WAIT=19 🡪24 表示有服务器端主动断开了5个TCP连接,即5个请求被瞬时拒绝
接着查看 /var/log/nginx/access.log日志
可以观察到在50分46秒,即压测第1秒时,成功处理了1个请求,另外有4个请求瞬间返回了503,剩下的5个请求每隔6s处理一次。
这是因为设置了burst=5,在服务器接收到10个并发请求后,先处理1个请求,同时将5个请求放入burst缓冲队列中,等待处理。而超过(burst+1)数量的请求就被直接抛弃了,即直接抛弃了4个请求。
3. 加burst和加nodelay的情况:
查看当前的tcp连接数
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
结果如下:
使用ab测试工具,发起10个并发请求:
ab -n 10 -c 10 url
可以看到压测在0.001s内完成了,这也是添加nodelay参数的意义
ab工具返回结果:
压测中一共10个请求,有4个请求失败了,直接返回了503
接着查看当前的tcp连接数:所有的请求都在1s内完成了
接着查看 /var/log/nginx/access.log日志
可以发现在1s内,服务器端处理了6个请求(峰值速度:burst+原来的处理速度)。对于剩下的4个请求,直接返回503,在下一秒如果继续向服务端发送10个请求,服务端会直接拒绝这10个请求并返回503。因为设定了没6s处理1个请求,所以直到30 s 之后,才可以再处理一个请求,即如果此时向服务端发送10个请求,会返回9个503,一个200
查看/var/log/nginx/error.log日志,发现有4个请求被直接拒绝了,没有延时请求。
总结
limit_req zone=req_zone;
严格依照在limti_req_zone中配置的rate来处理请求
超过rate处理能力范围的,直接drop
表现为对收到的请求无延时
limit_req zone=req_zone burst=5;
依照在limti_req_zone中配置的rate来处理请求
同时设置了一个大小为5的缓冲队列,在缓冲队列中的请求会等待慢慢处理
超过了burst缓冲队列长度和rate处理能力的请求被直接丢弃
表现为对收到的请求有延时
limit_req zone=req_zone burst=5 nodelay;
依照在limti_req_zone中配置的rate来处理请求
同时设置了一个大小为5的缓冲队列,当请求到来时,会爆发出一个峰值处理能力,对于峰值处理数量之外的请求,直接丢弃
在完成峰值请求之后,缓冲队列不能再放入请求。如果rate=10r/m,且这段时间内没有请求再到来,则每6s缓冲队列就能回复一个缓冲请求的能力,直到回复到能缓冲5个请求位置。
2)限制并发连接数
limit_conn_zone 中 1MB 的内存空间可以存储 32,000 个 32B 或 16,000 个 64B 的变量计数状态。
变量计数状态在 32 位系统平台占用 32B 或 64B,在 64 位系统平台占用 64B。
10M 的话换算下来可以保留 16 万个 IP 地址信息。
以下是基于 IP 做连接限制的示例,限制同一 IP 并发为 1:
# 设置内存区域,用于存储变量计数状态
#nginx
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /abc {
# 限制同一 IP 并发连接数为 1
limit_conn addr 1;
}
}
在这个配置中,我们使用了 Nginx 的 limit_conn_zone 和 limit_conn 指令来限制并发连接数。limit_conn_zone 指令用于定义一个内存区域来存储变量计数状态,而 limit_conn 指令用于限制每个 IP 的并发连接数。在这个例子中,我们限制了同一 IP 并发连接数为 1。
3)限制下载速度
将下载速度设置为 100k:
# 设置下载速度为 100k
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /abc {
# 限制下载速度为 100k
limit_rate 100k;
}
}
4)综合案例
# 限制用户连接数来预防 DOS 攻击
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
listen 80;
server_name localhost;
# 根路径和默认文件设置
location / {
root html;
index index.html index.htm;
# 限制同一客户端 IP 最大并发连接数
limit_conn perip 2;
# 限制同一 server 最大并发连接数
limit_conn perserver 20;
# 限制下载速度,根据自身服务器带宽配置
limit_rate 300k;
}
# 对 /abc 路径下的请求进行限制
location /abc {
# 对突发超过 5 个以后的请求放入缓存区
limit_req zone=one burst=5 nodelay;
# 限制并发连接数为 4
limit_conn addr 4;
# 限制下载速度为 100K
limit_rate 100k;
}
}
15、拒绝非法请求
网站通常使用 HTTP 协议进行通信。这个协议定义了多种方法,以便用户可以连接服务器并获取所需的资源。然而,在实际应用中,通常只需要使用 GET 和 POST 方法。为了提高安全性,我们可以配置服务器以拒绝非法请求,只允许 GET 和 POST 方法。这样可以降低潜在的安全风险。
具体HTTP请求方法的含义如表-1所示。
未修改服务器配置前,客户端使用不同请求方法测试:
[root@k8s-master nginx]# curl -I -X GET http://10.75.78.212 #正常
[root@k8s-master nginx]# curl -I -X HEAD http://10.75.78.212 #正常
#curl命令选项说明:
#-i选项:访问服务器页面时,显示HTTP的头部信息
#-X选项:指定请求服务器的方法
通过如下设置可以让Nginx拒绝非法的请求方法:
[root@proxy ~]# vim /usr/local/nginx/conf/nginx.conf
# 通过如下设置,可以让 Nginx 拒绝非法的请求方法
http {
server {
# 监听 80 端口
listen 80;
# 这里,'!' 符号表示对正则取反,'~' 符号是正则匹配符号
# 如果用户使用非 GET 或 POST 方法访问网站,则返回 444 错误信息
if ($request_method !~ ^(GET|POST)$) {
return 444;
}
}
}
[root@proxy ~]# /usr/local/nginx/sbin/nginx -s reload
修改后测试:
[root@k8s-master nginx]# curl -I -X HEAD http://10.75.78.212 #报错
[root@k8s-master nginx]# curl -I -X GET http://10.75.78.212 #正常
16、防御缓冲区buffer溢出
当客户端连接服务器时,服务器会启用各种缓冲区,用来存放连接的状态信息。如果攻击者发送大量的连接请求,而服务器不对缓冲区做限制的话,内存数据就有可能溢出(空间不足)。
缓冲区溢出攻击是通过将数据写入缓冲区并超过缓冲区的边界,覆盖进程的内存片段来实现的。为了防止在 Nginx 中出现这种情况,我们可以为所有客户端设置缓冲区大小限制。
如果 Nginx 使用了整个服务器内存,那么大量的 POST 请求可以有效地导致 DoS(拒绝服务)攻击。允许将大文件上传到服务器可以使攻击者更容易利用系统资源并成功执行拒绝服务攻击。
相应的值取决于服务器内存以及通信量。在选择合适的缓冲区大小时,可以参考以下公式:
MAX_MEMORY = client_body_buffer_size x CONCURRENT_TRAFFIC - OS_RAM - FS_CACHE
选择较小的 client_body_buffer_size(略大于 10k,但不是太多)会更好,因为更大的缓冲区可能会增加 DoS 攻击的风险,因为您将为它分配更多的内存。
提示:如果请求体大于 client_body_buffer_size,它将被写入磁盘,在内存中不可用,因此没有 $request_body。此外,将 client_body_buffer_size 设置过高可能会影响日志文件的大小(如果您记录 $request_body)。
通过修改 Nginx 配置文件,调整各种缓冲区参数,可以有效降低溢出风险。
[root@proxy ~]# vim /usr/local/nginx/conf/nginx.conf
http {
client_body_buffer_size 128k; # 默认值: 8k (32-bit) | 16k (64-bit)
client_header_buffer_size 512k; # 默认值: 1k
client_max_body_size 10m; # 默认值: 1m
large_client_header_buffers 4 512k; # 默认值: 4 8k
}
与之前的 proxy_cache 不同,这里的配置主要针对缓冲区的大小进行设置,以防止缓冲区溢出攻击。而 proxy_cache 是用于代理服务器的缓存设置,以提高网站性能。两者都是为了提高网站的安全性和性能,但侧重点不同。
17、try_files
使用 try_files 指令可以确保文件存在,并作出相应的处理。
建议使用 try_files 代替 if 指令,因为 if 指令的效率较低,它需要对每个请求都进行判断。相比之下,使用 try_files 的优点是:只需一个命令就可以立即切换行为,且代码更易读。
try_files 指令允许你:
检查文件是否存在于预定义列表中
检查指定目录中是否存在该文件
如果没有找到任何文件,则使用内部重定向
不建议的配置:
server {
...
root /var/www/example.com;
location /images {
# 如果请求的文件存在,设置过期时间为 30 天,然后终止处理
if (-f $request_filename) {
expires 30d;
break;
}
...
}
建议的配置:
server {
...
root /var/www/example.com;
location /images {
# 尝试请求 URL 对应的文件,如果文件不存在,则返回 404 错误
try_files $uri =404;
...
}
此外,try_files 还可以实现重定向:
location / {
# 设置资源根目录
root /usr/share/nginx/www;
# 设置默认索引文件
index index.html index.htm;
# 如果请求的文件是 index.html,则添加 Cache-Control 响应头,防止首页缓存
if ($request_filename ~* "index\.html$") {
add_header Cache-Control "no-cache, no-store";
}
# 尝试请求与 URL 对应的文件,如果不存在,尝试请求 URL 对应的目录(加斜杠)
# 如果目录也不存在,则返回 /index.html 文件
try_files $uri $uri/ /index.html;
}
在这段配置中,我们使用了 try_files 指令来实现重定向功能:
首先尝试请求与 URL 对应的文件($uri),如果文件存在,则直接返回该文件;
如果文件不存在,尝试请求 URL 对应的目录($uri/),如果目录存在,则返回该目录下的 index.html 文件;
如果目录也不存在,则返回 /index.html 文件。
这样,我们可以通过 try_files 指令实现简单的重定向功能。
18、使用return代替rewrite重定向
在 NGINX 中,重写 URL 的能力是一个非常强大和重要的特性。从技术角度讲,return 和 rewrite 均能实现重定向。然而,使用 return 相对于 rewrite 更简单且更快,因为计算正则表达式会产生额外的系统开销。
return 指令可以立即停止处理请求(它会直接停止执行)并将指定的代码返回给客户端,省略了正则表达式计算的流程。
如果你需要用正则表达式验证 URL,或者需要获取原始 URL 中的元素(显然不在相应的 NGINX 变量中),那么你应该使用 rewrite。
不建议实现方式
server {
...
location / {
# 使用正则表达式重写 URL,将所有请求重定向到 https://example.com/,并保留原始路径
rewrite ^/(.*)$ https://example.com/$1 permanent;
}
...
}
这里担心有同学不懂,我详细解释下:
rewrite ^/(.*)$ https://example.com/$1 permanent;:这是一个 rewrite 指令,用于重写请求 URL。它包含三个部分:
^/(.*)$:这是一个正则表达式,用于匹配请求的 URL 路径。^/ 表示路径以 / 开头,(.*) 表示匹配任意字符(0 个或多个),$ 表示路径的结束。所以这个正则表达式会匹配所有以 / 开头的路径,并将路径中除 / 之外的部分捕获到一个名为 $1 的变量中。
https://example.com/$1:这是重定向的目标 URL。它使用 https://example.com 作为新域名,并使用之前捕获的 $1 变量保留原始请求路径。例如,如果原始请求路径是 /path/to/resource,那么重定向后的 URL 将是 https://example.com/path/to/resource。
permanent:这是一个标志,表示我们使用 301 永久重定向。这意味着浏览器和搜索引擎会记住这个重定向,并将原始 URL 替换为新的 URL。
建议实现方式
server {
...
location / {
# 尝试请求与 URL 对应的文件,如果不存在,尝试请求 URL 对应的目录
# 如果目录也不存在,则返回 404 错误
try_files $uri $uri/ =404;
# 重定向所有请求到 https://example.com/,并保留原始路径,使用 301 状态码
return 301 https://example.com$request_uri;
}
...
}
在建议的实现方式中,我们使用 return 指令进行重定向,相较于使用正则表达式的 rewrite,这种方式更简单且更快。同时,我们保留了原始请求的路径,以便在新的域名下正确访问。
19、upstream开启keepalive
keepalive 指令在 upstream 中用于设置与后端服务器之间的持久连接。它可以在一定时间内保持与后端服务器的连接打开状态,以便更快地处理后续请求。这可以提高性能并减少资源消耗。
配置 keepalive 的主要目的是解决在高延迟网络上建立 TCP 连接的延迟问题。当 NGINX 与上游服务器之间需要持续保持一定数量的连接时,keepalive 很有用。
开启 Keep-Alive 连接对性能有很大的影响:减少了打开和关闭连接所需的 CPU 和网络开销。
通过在 NGINX 中启用 HTTP keepalive,降低了 NGINX 连接上游服务器的延迟,从而提高了性能,并减少了 NGINX 耗尽临时端口的可能性。NGINX 将重用现有的 TCP 连接,而不是创建新的 TCP 连接,这可以极大地减少繁忙服务器上处于 TIME_WAIT 状态的 TCP 连接中的套接字数量(减少操作系统建立新连接的工作,减少网络上的数据包)。
注意:仅在 HTTP/1.1 时支持 Keep-Alive 连接。
配置样例
Upstream context:
upstream backend {
# 设置每个 worker 进程缓存中保留的空闲 keepalive 连接到上游服务器的最大数量
keepalive 16;
}
Server/location contexts:
server {
...
location / {
# 默认情况下,仅在与上游通信时使用 HTTP/1,
# 但 keepalive 仅在 HTTP/1.1 中启用:
proxy_http_version 1.1;
# 如果客户端发送了 Connection 头,将其删除,
# 因为它可能是 "close",用于关闭 keepalive 连接:
proxy_set_header Connection "";
...
}
}
在这个配置样例中,我们在 upstream 中设置了 keepalive 参数,并在 location 中启用了 HTTP/1.1 版本,以便使用 keepalive 功能。同时,我们移除了客户端发送的 Connection 头,防止其关闭 keepalive 连接。这样,我们可以在 NGINX 与后端服务器之间建立持久连接,从而提高性能。
20、尽可能精确地使用location
精确的 location 匹配通常被用来加快选择过程,匹配成功后立即结束算法的执行。
使用 = 修饰符可以定义 URI 和 location 的精确匹配。它的处理速度非常快,可以节省大量的 CPU 开销。
示例 1:精确匹配根路径
location = / {
...
}
这个示例中的 location 块仅匹配根路径 /,当请求路径为 / 时,会立即执行此块中的配置。
示例 2:精确匹配 /v9 路径
location = /v9 {
...
}
这个示例中的 location 块仅匹配 /v9 路径,当请求路径为 /v9 时,会立即执行此块中的配置,然后停止搜索其他 location 块。
通过使用精确匹配的 location,您可以提高 NGINX 选择 location 的效率,从而提高服务器性能。
这里我举例说明下不使用精确匹配
1.前缀匹配:这是 location 块的默认匹配方式,它会匹配以指定前缀开头的所有请求路径。
location /images/ {
...
}
在这个示例中,location 块会匹配所有以 /images/ 开头的请求路径,如 /images/logo.png、/images/banner.jpg 等。
2.正则表达式匹配:使用 ~ 或 ~* 修饰符定义正则表达式匹配。~ 表示区分大小写的匹配,而 ~* 表示不区分大小写的匹配。
location ~* \.(jpg|jpeg|png|gif)$ {
...
}
在这个示例中,location 块会匹配所有以 .jpg、.jpeg、.png 或 .gif 结尾的请求路径,不区分大小写。
3.通配符匹配:使用 ^~ 修饰符定义通配符匹配。这将匹配指定的前缀,但如果找到匹配,NGINX 将停止搜索正则表达式 location。
location ^~ /static/ {
...
}
在这个示例中,location 块会匹配所有以 /static/ 开头的请求路径。如果找到匹配,NGINX 将不再搜索正则表达式 location。
这些非精确匹配的 location 块在实际应用中非常常见,它们可以根据您的需求灵活地匹配各种请求路径。然而,相较于精确匹配,它们的匹配效率略低。
21、rewrite重写
在 Kubernetes Ingress 中引入重写:
nginx.ingress.kubernetes.io/rewrite-target /$1 是一个 Kubernetes Ingress Controller 的注解,它指示 Nginx Ingress Controller 重写请求的 URI,将其替换为 / 后面的第一个捕获组的值。例如,如果请求的 URI 是 /foo/bar,则它将被重写为 /bar。
在 NGINX 中进行类似的重写,可以使用 rewrite 指令。例如,要将 /foo/bar 重写为 /bar,可以使用以下配置:
location /foo {
# 使用正则表达式匹配请求路径,并将 /foo/ 后面的部分捕获到 $1 变量中
# 将请求路径重写为以 / 开头的捕获组内容,并使用 break 停止后续重写规则
rewrite ^/foo/(.*)$ /$1 break;
}
这将捕获 /foo/ 后面的任何内容,并将其替换为 / 后面的内容。break 参数指示 Nginx 停止处理当前请求,并将其发送到重写后的 URI。在这个示例中,例如请求路径 /foo/bar 将被重写为 /bar。
四、内核参数优化
# 进入这个文件修改
vim /etc/sysctl.conf
# 让修改的内核信息生效
sysctl -p
# 配置内容如下:
fs.file-max = 999999 # 进程(如 worker 进程)可以同时打开的最大句柄数,限制最大并发连接数
net.ipv4.ip_forward = 0 # 禁用 IP 数据包转发
net.ipv4.conf.default.rp_filter = 1 # 默认启用源 IP 地址检查,防止 IP 地址欺骗
net.ipv4.conf.default.accept_source_route = 0 # 默认禁止接受源路由 IP 数据包
kernel.sysrq = 0 # 禁止使用 SysRq 键
kernel.core_uses_pid = 1 # 使用进程 ID 命名 core dump 文件
net.ipv4.tcp_syncookies = 1 # 开启 SYN Cookies,当出现 SYN 等待队列溢出时,启用 cookies 来处理
kernel.msgmnb = 65536 # 系统级消息队列的最大字节数
kernel.msgmax = 65536 # 单个消息队列的最大字节数
kernel.shmmax = 68719476736 # 共享内存段的最大字节数
kernel.shmall = 4294967296 # 共享内存总量的最大页数
net.ipv4.tcp_max_tw_buckets = 6000 # 操作系统允许 TIME_WAIT 套接字数量的最大值,超过此数,TIME_WAIT 套接字将立即被清除并打印警告信息
net.ipv4.tcp_sack = 1 # 启用 TCP Selective Acknowledgments,提高网络性能
net.ipv4.tcp_window_scaling = 1 # 启用 TCP 窗口缩放,以便在高延迟连接上获得更好的吞吐量
net.ipv4.tcp_rmem = 10240 87380 12582912 # TCP 接受缓存(用于 TCP 接受滑动窗口)的最小值、默认值、最大值
net.ipv4.tcp_wmem = 10240 87380 12582912 # TCP 发送缓存(用于 TCP 发送滑动窗口)的最小值、默认值、最大值
net.core.wmem_default = 8388608 # 内核套接字发送缓存区默认的大小
net.core.rmem_default = 8388608 # 内核套接字接受缓存区默认的大小
net.core.rmem_max = 16777216 # 内核套接字接受缓存区的最大大小
net.core.wmem_max = 16777216 # 内核套接字发送缓存区的最大大小
net.core.netdev_max_backlog = 262144 # 每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目
net.core.somaxconn = 40960 # listen 函数的 backlog 默认会给我们内核参数的限制到 128,而 nginx 定义的 NGX_LISTEN_BACKLOG 默认为 511,所以有必要调整这个值
net.ipv4.tcp_max_orphans = 3276800 # 允许未连接的 TCP 套接字的最大数量,超过这个数量,系统将立即关闭新的 TCP 套接字
net.ipv4.tcp_max_syn_backlog = 262144 # TCP 三次握手建立阶段接受 SYN 请求队列的最大长度
net.ipv4.tcp_timestamps = 0 # 禁用 TCP 时间戳,以减少 CPU 使用率
net.ipv4.tcp_synack_retries = 1 # 减少 SYN+ACK 段的重试次数,以减小对于 SYN Flood 攻击的敏感性
net.ipv4.tcp_syn_retries = 1 # 减少 SYN 段的重试次数,以减小对于 SYN Flood 攻击的敏感性
net.ipv4.tcp_tw_recycle = 1 # 启用 timewait 快速回收
net.ipv4.tcp_tw_reuse = 1 # 开启重用,允许将 TIME-WAIT sockets 重新用于新的 TCP 连接
net.ipv4.tcp_mem = 94500000 915000000 927000000 # 指定 TCP 协议栈应该为 TCP 套接字分配多少内存
net.ipv4.tcp_fin_timeout = 1 # 减少 FIN_WAIT2 状态的超时时间,以避免资源浪费
net.ipv4.tcp_keepalive_time = 30 # 当 keepalive 启用时,TCP 发送 keepalive 消息的频度,默认为 2 小时
net.ipv4.ip_local_port_range = 1024 65000 # 允许系统打开的端口范围
五、安全加固
1、安装最新版nginx
建议安装最新版本的nginx,因为nginx本身向后兼容,使用最新版本可以获得更好的性能和更多的功能。
2、使用非特权用户运行nginx
在Linux系统中,一个通用的重要原则是程序只应该拥有完成其工作所需的最小权限。因此,建议使用非特权用户来运行nginx,这样即使nginx发生故障,它对系统的损害也会受到限制。
需要注意的是,仅仅通过更改进程所有者名称并不足以提高安全性。最小特权原则规定,进程实体在给定系统中实现其目标所必需的权限之外,不应该被授予更多的权限。因此,只有master进程作为root进程运行,其他进程应该使用非特权用户来运行。
[root@localhost ~]# ps -ef|grep nginx
root 1049 1 0 00:02 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 1051 1049 0 00:02 ? 00:00:00 nginx: worker process
nginx 1052 1049 0 00:02 ? 00:00:00 nginx: cache manager process
nginx 1053 1049 0 00:02 ? 00:00:00 nginx: cache loader process
root 1317 1297 0 00:03 pts/0 00:00:00 grep --color=auto nginx
如果使用这个仓库:nginx 的安装包进行安装,关于最小权限的配置已配置完毕:
master进程运行user: root
worker进程运行user: nginx
目录权限
/var/log/nginx: 日志目录(nginx:nginx)
/var/cache/nginx: 缓存目录(nginx:nginx)
/var/dump/nginx: dump目录(nginx:nginx)
/etc/nginx: 配置目录(nginx:nginx)
/usr/share/nginx: 静态文件目录(nginx:nginx)
3、保护敏感资源
在web服务器中,隐藏的目录和文件永远不应该被公开访问。有时,关键数据会在应用程序部署期间被意外发布。如果使用版本控制系统,确保在发布应用程序时明确禁止对关键隐藏目录/文件的访问,例如.git或.svn,以防止暴露应用程序的源代码。
下面是一个保护敏感资源的示例配置,您可以将其添加到nginx配置文件中:
server {
listen 8088; # 监听端口
include /etc/nginx/conf/conf.d/deny.location; # 包含敏感资源保护配置
location / {
return 200; # 返回状态码
}
}
然后在/etc/nginx/conf/conf.d/deny.location文件中添加以下内容:
location ~* ^.*(\.(?:git|svn|hg|bak|bckp|save|old|orig|original|test|conf|cfg|dist|in[ci]|log|sql|mdb|sw[op]|htaccess|php#|php~|php_bak|aspx?|tpl|sh|bash|bin|exe|dll|jsp|out|cache|))$ {
# 匹配敏感文件和目录的正则表达式
# 如果需要,可以根据实际情况修改表达式
# 可以启用限速:
# 在server上下文中使用:limit_req_zone $binary_remote_addr zone=per_ip_5r_s:5m rate=5r/s;
limit_req zone=per_ip_5r_s;
deny all; # 禁止访问敏感资源
access_log /var/log/nginx/restricted-files-access.log main; # 记录访问日志
error_log /var/log/nginx/restricted-files-error.log; # 记录错误日志
}
此配置将禁止访问匹配指定正则表达式的文件和目录,并记录访问尝试和错误日志。建议根据实际需求进行适当的调整和优化。
4、隐藏nginx版本信息
为了提高安全性,建议隐藏Nginx版本信息,以防止攻击者利用特定版本的漏洞进行攻击。要隐藏Nginx版本信息
将以下指令添加到配置文件的http或server块中:
http {
...
server_tokens off; # 禁止在HTTP响应头中显示Nginx版本信息
...
}
server {
...
server_tokens off; # 禁止在HTTP响应头中显示Nginx版本信息
...
}
将此指令添加到配置文件后,即可禁止在HTTP响应头中显示Nginx版本信息。这有助于降低攻击者通过收集信息来针对您的服务器的风险。
5、隐藏上游代理header头信息
当Nginx被用作反向代理上游服务器(例如PHP-fpm实例)时,隐藏上游响应中发送的某些头信息(如PHP运行的版本)是有益的。这可以通过使用proxy_hide_header指令(或Lua模块)来实现,从而隐藏或删除上游服务器返回到Nginx反向代理(并最终返回到客户端)的头信息。
将以下内容添加到代理HTTP服务的location块中,并包含在/etc/nginx/conf/conf.d/hide-headers.rule文件中。
upstream ddd-server {
server 11.11.11.11:80; # 上游服务器1
server 11.11.11.12:80; # 上游服务器2
}
server {
listen 8081; # 监听端口
location /ddd {
include /etc/nginx/conf/conf.d/hide-headers.rule; # 包含隐藏头信息的配置文件
proxy_pass http://ddd-server; # 反向代理到上游服务器
}
}
然后,创建并编辑/etc/nginx/conf/conf.d/hide-headers.rule文件,添加以下内容:
proxy_hide_header X-Application-Context; # 隐藏 X-Application-Context 头信息
proxy_hide_header Access-Control-Allow-Origin; # 隐藏 Access-Control-Allow-Origin 头信息
proxy_hide_header X-Powered-By; # 隐藏 X-Powered-By 头信息
proxy_hide_header X-AspNetMvc-Version; # 隐藏 X-AspNetMvc-Version 头信息
proxy_hide_header X-Drupal-Cache; # 隐藏 X-Drupal-Cache 头信息
proxy_hide_header X-Powered-By; # 隐藏 X-Powered-By 头信息
proxy_hide_header Server; # 隐藏 Server 头信息
proxy_hide_header X-AspNet-Version; # 隐藏 X-AspNet-Version 头信息
proxy_hide_header X-Drupal-Dynamic-Cache; # 隐藏 X-Drupal-Dynamic-Cache 头信息
proxy_hide_header X-Generator; # 隐藏 X-Generator 头信息
proxy_hide_header X-Runtime; # 隐藏 X-Runtime 头信息
proxy_hide_header X-Rack-Cache; # 隐藏 X-Rack-Cache 头信息
通过添加以上配置,将隐藏或删除与上游服务器相关的敏感头信息,从而提高服务器的安全性。根据实际需求,可以添加或删除更多的头信息。
6、禁用不安全的http方法
通常,Web服务支持GET、HEAD和POST方法来检索静态和动态内容。公开的Web服务不应支持其他HTTP方法(例如OPTIONS、TRACE),因为这增加了攻击面。在生产环境中,建议禁用这些方法(除非您确实需要使用它们)。
- TRACE方法安全隐患:启用TRACE方法可能导致跨站点跟踪攻击,攻击者可以捕获其他应用程序用户的会话ID。此外,该方法还可以用于尝试获取有关应用程序操作环境的附加信息(例如,应用程序路径上是否存在缓存服务器)。
- OPTIONS方法安全隐患:虽然启用OPTIONS方法不会产生直接威胁,但攻击者可以从OPTIONS方法响应中获取额外信息,进而利用已知漏洞进行攻击。
- HEAD方法安全隐患:启用HEAD方法也存在风险,尽管它并不被认为是危险的,但它可以通过模仿GET请求来攻击Web应用程序。此外,使用HEAD方法可以通过限制服务器发送的数据量来加快攻击进程。如果授权机制基于GET和POST,那么HEAD方法可能允许绕过这些保护。
要通过Nginx拦截HTTP方法,请参考以下推荐配置:
server {
...
# 在 server 上下文中,建议使用以下结构:
add_header Allow "GET, HEAD, POST" always;
if ($request_method !~ ^(GET|HEAD|POST)$) {
# 您也可以在 'if' 上下文中使用 'add_header':
# add_header Allow "GET, HEAD, POST" always;
return 405; # 返回状态码 405(方法不允许)
}
}
7、避免敏感数据的缓存
尽管缓存策略应该由应用本身实现,但在很多情况下,反向代理服务仍需要进行处理。为了确保敏感数据不被缓存或持久化,建议在包含敏感信息的页面中添加Cache-Control头。
实现方法:
在响应中添加防缓存头,例如:Cache-Control: no-cache, no-store和Expires: 0。
为了兼容多种浏览器实现,建议配置以下响应头:
- Cache-Control: no-cache, no-store, private, must-revalidate, max-age=0, no-transform
- Pragma: no-cache
- Expires: 0
以下是一个Nginx配置示例,基于location上下文:
location /api {
expires 0;
add_header Pragma "no-cache";
add_header Cache-Control "no-cache, no-store, private, must-revalidate, max-age=0, no-transform";
}
通过添加以上配置,将避免缓存敏感数据,从而提高应用程序的安全性。请根据实际需求调整和优化配置。
8、缓解HTTP慢速Dos攻击
通过限制连接的存活时间(keepalive_timeout)和发送超时(send_timeout),可以缓解慢速DoS攻击。过长的keepalive时间将降低服务器接受新连接的能力,而过高的发送超时可能导致资源浪费。
对于大多数场景,2-3秒的keepalive_timeout已经足够解析HTML/CSS并检索所需的图像和图标。设置较低的send_timeout将使Web服务器更快地关闭连接,释放资源,供新连接使用,从而提高系统吞吐量。需要注意的是,超时配置应根据实际情况进行调整,部分请求可能需要较长的时间接收响应,这时可以在location上下文中单独配置超时时间。
以下是一些参考值的配置示例:
client_body_timeout 10s; # 默认值:60s
client_header_timeout 10s; # 默认值:60s
keepalive_timeout 5s 5s; # 默认值:75s
send_timeout 10s; # 默认值:60s
六、实战配置
1、内核参数配置
vim /etc/sysctl.conf
sysctl -p
编辑内核参数配置文件:
vim /etc/sysctl.conf
在配置文件中添加以下内容:
# 降低 TIME_WAIT 状态的连接数量,以减小 tcp_max_tw_buckets 值(默认值为 180000)
net.ipv4.tcp_max_tw_buckets = 5000
# 开启连接重用功能,允许将 TIME-WAIT 状态的 sockets 重新用于新的 TCP 连接
net.ipv4.tcp_tw_reuse = 1
# 设置系统中最多允许有多少个 TCP 套接字不关联到任何用户文件句柄。超过此数值的孤儿连接将被立即复位并打印警告信息。此限制仅用于防止简单的 DoS 攻击。
net.ipv4.tcp_max_orphans = 262144
# 当启用 keepalive 时,调整 TCP 发送 keepalive 消息的频率。默认值为 2 小时,可以根据需要缩短时间间隔。
net.ipv4.tcp_keepalive_time = 30
然后运行以下命令使配置生效:
sysctl -p
这些配置有助于优化 TCP 连接性能,降低 TIME_WAIT 状态的连接数量,防止简单的 DoS 攻击,并提高 keepalive 机制的效率。请根据实际需求调整这些值。
2、nginx全局配置
worker_processes 1;
pid /var/run/nginx.pid;
events {
worker_connections 2048; # 设置每个 worker 进程允许的最大并发连接数
multi_accept on; # 允许 worker 进程在同一时间处理多个连接
use epoll; # 使用 epoll 事件模型,提高效率
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 自定义日志格式
log_format main '{"@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",'
'"xff":"$http_x_forwarded_for",'
'"referer":"$http_referer",'
'"agent":"$http_user_agent",'
'"status":"$status"}';
sendfile on; # 开启 sendfile 优化文件传输
tcp_nopush on; # 开启 tcp_nopush,优化数据包发送
tcp_nodelay on; # 开启 tcp_nodelay,减少网络延迟
# 设置服务器名称相关参数
server_names_hash_bucket_size 128;
server_names_hash_max_size 512;
# 设置超时参数
keepalive_timeout 65;
client_header_timeout 15s;
client_body_timeout 15s;
send_timeout 60s;
# 配置连接数和速率限制
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
limit_conn perip 2;
limit_conn perserver 20;
limit_rate 300k;
# 配置代理缓存路径
proxy_cache_path /data/nginx-cache levels=1:2 keys_zone=nginx-cache:20m max_size=50g inactive=168h;
# 配置缓冲区大小
client_body_buffer_size 512k;
client_header_buffer_size 4k;
client_max_body_size 512k;
large_client_header_buffers 2 8k;
# 配置代理超时
proxy_connect_timeout 5s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
# 配置代理缓冲区
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
# 配置代理重试策略
proxy_next_upstream http_502 http_504 http_404 error timeout invalid_header;
# 配置 gzip 压缩
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 4;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
gzip_disable "MSIE [1-6].";
include /etc/nginx/conf.d/*.conf;
}
4、server配置
这个server配置的目的是隐藏Nginx版本信息并将HTTP请求重定向到HTTPS。
# 隐藏 Nginx 版本信息
server_tokens off;
server {
listen 80;
server_name www.ygoclub.com;
charset utf-8;
# 重定向 HTTP 请求到 HTTPS
return 301 https://$server_name$request_uri;
}
5、HTTPS配置(ssl配置)
这里主要是配置SSL证书以及相关设置
# 配置HTTPS服务器
server {
listen 443 ssl; # 监听443端口,启用SSL
server_name example.com; # 使用您的域名替换
root /xxx/xxx/html; # 前台文件存放目录
index index.html;
# SSL证书配置
ssl_certificate /path/to/your_certificate.pem; # 请替换为实际的证书路径
ssl_certificate_key /path/to/your_certificate.key; # 请替换为实际的证书密钥路径
ssl_session_timeout 5m; # 设置SSL会话超时时间
# SSL协议配置
ssl_protocols TLSv1.2; # 仅使用TLSv1.2,以提高安全性
ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'; # 使用安全的加密套件
ssl_prefer_server_ciphers on; # 优先使用服务器端的加密套件
location / {
proxy_redirect off; # 禁用代理重定向
proxy_set_header Host $host; # 设置代理请求头中的Host字段
proxy_set_header X-Real-IP $remote_addr; # 设置代理请求头中的X-Real-IP字段
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 设置代理请求头中的X-Forwarded-For字段
proxy_pass http://127.0.0.1:8080; # 将请求转发给本地的8080端口
add_header Content-Type text/plain; # 设置HTTP响应头中的Content-Type字段
add_header X-Content-Type-Options nosniff; # 设置HTTP响应头中的X-Content-Type-Options字段
# 防止首页缓存
if ($request_filename ~* "index\.html$") {
add_header Cache-Control "no-cache, no-store";
}
try_files $uri $uri/ /index.html; # 如果找不到文件或目录,则重定向到/index.html
}
}
# 配置HTTP服务器,重定向所有HTTP请求到HTTPS
server {
listen 80;
server_name example.com; # 使用您的域名替换
return 301 https://$host$request_uri; # 把HTTP请求重定向到HTTPS
}
七、其他
1、Apache自带压力测试工具
压力测试的概念
ab全称为:apache bench
压力测试的概念:
吞吐率(Requests per second)
概念:服务器并发处理能力的量化描述,单位是reqs/s,指的是某个并发用户数下单位时间内处理的请求数。某个并发用户数下单位时间内能处理的最大请求数,称之为最大吞吐率。
计算公式:总请求数 / 处理完成这些请求数所花费的时间,即 Request per second = Complete requests / Time taken for tests
并发连接数(The number of concurrent connections)
概念:某个时刻服务器所接受的请求数目,简单的讲,就是一个会话。
并发用户数(The number of concurrent users,Concurrency Level)
概念:要注意区分这个概念和并发连接数之间的区别,一个用户可能同时会产生多个会话,也即连接数。
用户平均请求等待时间(Time per request) 计算公式:处理完成所有请求数所花费的时间/ (总请求数 / 并发用户数),即
Time per request = Time taken for tests /( Complete requests /Concurrency Level)
服务器平均请求等待时间(Time per request: across all concurrent requests)
计算公式:处理完成所有请求数所花费的时间 / 总请求数,即 Time taken for / testsComplete requests
可以看到,它是吞吐率的倒数。 同时,它也=用户平均请求等待时间/并发用户数,即 Time per request /Concurrency Level
Apache Benchmark(简称ab) 是Apache安装包中自带的压力测试工具 ,简单易用
ab压力测试工具的原理
ab的原理:ab命令会创建 多个并发 访问线程,模拟 多个访问者 同时对某一 URL地址 进行访问。
它的测试目标是基于URL的,因此,它既可以用来测试apache的负载压力,也可以测试nginx、lighthttp、tomcat、IIS等其它Web服务器的压力。
ab是一个命令行工具, ab命令对发出负载的计算机要求很低,它既不会占用很高CPU,也不会占用很多内存。但却会给目标服务器造成巨大的负载,其原理类似CC攻击。自己测试使用也需要注意,否则一次上太多的负载。可能造成目标服务器资源耗完,严重时甚至导致死机。
总的来说ab工具小巧简单,上手学习较快,可以提供需要的基本性能指标,但是没有图形化结果,不能监控。
ab工具安装(linux)
linux yum安装
yum install httpd-tools
验证是否安装成功
[root@k8s-master ~]# ab -V
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/](http://www.apache.org/)
ab工具安装(window)
首先,需要安装 Apache 服务器
下载地址:https://www.apachelounge.com/download/
下载完毕后,得到如下目录:
直接双击abs.exe是无法打开的。
正确的方式是打开cmd命令,进入Apache Bench 的bin目录,启动abs.exe,如下图所示:
ab使用实例(windows)
参数很多,示例这里使用-n和-c参数就可以了。例如:
abs.exe -n 100 -c 20 测试网址
-n 后面的100,表示总共发出100 个请求;
-c 后面的20 ,表示采用20 个并发(模拟 20 个人同时访问),
后面的网址表示测试的目标URL。
$ ./abs.exe -n 100 -c 20 https://www.baidu.com/ (这里使用gitbash 如果使用cmd执行把./去掉即可)
Document Path:测试页面
Document Length: 页面大小
Concurrency Level: 测试的并发数
Time taken for tests:整个测试持续的时间
Complete requests:完成的请求数量
Failed requests: 失败的请求数量
Total transferred: 整个过程中的网络传输量
HTML transferred: 整个过程中的HTML内容传输量
Requests per second: 最重要的指标之一,相当于LR中的每秒事务数,后面括号中的mean表示这是一个平均值
Time per request: 最重要的指标之二,相当于LR中的平均事务响应时间,后面括号中的mean表示这是一个平均值
Time per request: 每个连接请求实际运行时间的平均值
Transfer rate: 平均每秒网络上的流量,可以帮助排除是否存在网络流量过大导致响应时间延长的问题
ab压力测试工具的使用(linux)
一般常用的 参加就两个:
• -n表示请求数
• -c表示并发数
ab -n 100 -c 10 https://www.baidu.com/
-n 要执行请求数,默认会执行一个请求
-c 一次执行多个请求的数量,默认是一次一个请求。
-t 用于基准测试的最大秒数,使用它在固定的总时间内对服务器进行基准测试。默认情况下,没有时间限制。
-s 超时之前等待的最大秒数。 默认值是30秒。
-b TCP发送/接收缓冲区的大小,以字节为单位。
-B 进行传出连接时要绑定的地址。
-p 包含数据到POST的文件。 还请记住设置-T。
-u 包含PUT数据的文件。 还请记住设置-T 。
-T Content-type用于POST / PUT数据的内容类型内容类型标题,例如:‘application/x-www-form-urlencoded’ 默认是’text/plain’
-v verbosity 要打印多少个疑难解答信息,设置详细级别 - 4和以上打印标题信息,3和以上打印响应代码(404,200等),2和以上打印警告和信息。
-w 在HTML表格中打印结果。
-i 使用HEAD代替GET。
-x 用作<table>的属性的字符串。 属性被插入<table here>。
-y 用作<tr>的属性的字符串。
-z 用作<td>的属性的字符串。
-C 将cookie添加到请求。 参数通常采用名称=值对的形式。 这个字段是可重复的。
-H attribute 例如 ‘Accept-Encoding: gzip’ 插入所有普通标题行之后。(重复)
-A 添加基本的WWW认证,该属性是一个冒号分隔的用户名和密码,auth-username:password
-P 添加基本代理验证,属性是一个冒号分隔的用户名和密码,proxy-auth-username:password
-X 使用代理服务器和端口号。
-V 打印版本号并退出。
-k 使用HTTP KeepAlive功能。
-d 不要显示百分点服务表。
-S 不要显示信心估计和警告。
-q 做超过150个请求时不要显示进度。
-g 将收集的数据输出到gnuplot格式文件。
-e 输出提供百分比的CSV文件。
-r 不要退出套接字接收错误。
-h 显示使用情况信息(此消息)。
-Z 密码套件指定SSL / TLS密码套件(请参阅openssl密码)
-f 指定SSL / TLS协议 (SSL3, TLS1, TLS1.1, TLS1.2 or ALL)
如何判断是不是被CC?一般被CC呢,宝塔上面显示负载跟CPU都会爆表100%的
下面以Linux系统为示列,Win系统,可直接查看任务管理器即可。
SSH登录服务器执行以下命令
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
执行结果后,会显示下面以下参数。
LAST_ACK 5
SYN_RECV 30
ESTABLISHED 1597
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057
以上参数随便一个大于200,再考虑是不是CC,如果都小于200,然后CPU 100%,在检查是否程序导致,或服务器配置不够等因素。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_40231055/article/details/131165216