当我需要进行性能优化时,说明我们服务器无法满足日益增长的业务。性能优化是一个比较大的课题,需要从以下几个方面进行探讨。
首先需要了解的是当前系统瓶颈,用的是什么,跑的什么业务。里面的服务是什么样子,每个服务最大支持多少并发。比如针对Nginx而言,我们处理静态资源最高的并发是多大。
虽然我们是在做性能优化,但还是要熟悉业务,最终目的都是为了业务服务的。我们要了解每一个接口业务类型是什么样的业务,
争抢机制示意图
一个master Process管理多个worker process,也就是说Nginx采用的是多进程结构,而不是多线程结构
当client发出请求(任务)时,master Process会通知管理的worker process
worker process开始争抢任务,争抢到的worker process会开启连接,完成任务
每个worker都是一个独立的进程,每个进程里只有一个主线程
Nginx采用了IO多路复用机制(需要在Linux环境),使用IO多路复用机制,是Nginx在使用为数不多的worker process就可以实现高并发的关键
master-worker机制示意图
举例来说:每进来一个request,会有一个 worker 进程去处理。但不是全程的处理,处理到什么程度呢?
处理到可能发生阻塞的地方,比如向上游 (后端) 服务器转发 request,并等待请求返回
那么,这个处理的 worker 不会这么傻等着,他会在发送完请求后,注册一个事件:“如果 upstream 返回了,告诉我一声,我再接着干”。于是他就休息去了
此时,如果再有 request 进来,他就可以很快再按这种方式处理
而一旦上游服务器返回了,就会触发这个事件,worker 才会来接手,这个 request 才会接着往下走
由于 web server 的工作性质决定了每个 request 的大部份生命都是在网络传输中,实际上花费在 server 机器上的时间片不多,这就是几个进程就能解决高并发的秘密所在
大家对相关的系统瓶颈及现状有了一定的了解之后,就可以根据邮箱性能方面做一个全体的优化。
linux/unix,一切皆文件,每一次用户发起请求就会生成一个文件句柄,文件句柄可以理解为就是一个索引,所有文件句柄就会随着请求量的增多,而进程调用的频率增加,文件句柄的产生就会越多,系统对文件句柄默认的限制是1024个,对于nginx来说非常小了,需要改大一点
ulimit -n #可以查看系统设置的最大文件句柄
系统全局性修改
用户局部性修改
进程局部性修改
vim /etc/security/limits.conf
soft:软控制,到达设定值后,操作系统不会采取措施,只是发提醒
hard:硬控制,到达设定值后,操作系统会采取机制对当前进行进行限制,这个时候请求就会受到影响
root:这里代表root用户(系统全局性修改)
*:代表全局,即所有用户都受此限制(用户局部性修改)
nofile:指限制的是文件数的配置项。后面的数字即设定的值,一般设置10000左右
尤其在企业新装的系统,这个地方应该根据实际情况进行设置,可以设置全局的,也可以设置用户级别的。
su #刷新以下环境 ulimit -n #再次查看系统最大文件句柄
这里就改为了,我们对root用户设置的最大文件句柄65535
vim /usr/local/nginx/conf/nginx.conf
每个进程的最大文件打开数,所以最好与ulimit -n的值保持一致。
重启一下服务
cpu的亲和能偶使nginx对于不同的work工作进程绑定到不同的cpu上面去。就能够减少在work间不断切换cpu,把进程通常不会在处理器之间频繁迁移,进程迁移的频率小,来减少性能损耗。
每个 worker 的线程可以把一个 cpu 的性能发挥到极致。所以 worker 数和服务器的 cpu 数相等是最为适宜的。设少了会浪费 cpu,设多了会造成 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;
重启一下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;
nginx的连接处理机制在于不同的操作系统会采用不同的I/O模型,Linux下,nginx使用epoll的I/O多路复用模型,在freebsd(类unix操作系统)使用kqueue的I/O多路复用模型,在Solaris(unix系统的一个重要分支操作系统)使用/dev/pool方式的I/O多路复用模型,在windows使用的icop等等。要根据系统类型不同选择不同的事物处理模型,我们使用的是centos,因此讲nginx的事件处理模型调整为epoll。
进入配置文件再进行修改
events { worker_connections 10240; multi_accept on; use epoll; }
work_connections表示每个worker(子进程)可以创建多少个连接,
默认:work_connections:1024
最大:work_connections:65535
同时要根据系统的最大打开文件数来调整
系统的最大打开文件数>= worker_connections*worker_process
根据系统的最大打开文件数来调整,worker_connections 进程连接数量要小于等于系统的最大打开文件数
worker_connections 进程连接数量真实数量= worker_connections * worker_process
查看系统的最大打开文件数:
ulimit -a | grep "open files"
这里我自己改为10240(这个参数与服务器性能相关)
worker_connections 10240;
如果是支持 http1.1 的浏览器每次访问要占两个连接,所以普通的静态访问最大并发数是:worker_connections * worker_processes /2
而如果是HTTP作为反向代理来说,最大并发数量应该是 worker_connections * worker_processes/4
因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,会各占用两个连接
看一个示意图
keepalive_timeout 65;
keepalive 其他的一些配置
keepalive:像上游服务器的保留连接数
keepalive_disable:是指把某些浏览器禁用掉
keepalive_requests:这个表示当我们建立一个可复用长连接,在当前这一个连接里可以并发接收多少个请求,默认1000个
send_timeout:在向客户端发送数据时,建立好连接后,超过两次活动时间(服务器很长时间没有返回数据)那么就会将这个连接关掉
keepalive_time:一个tcp连接的总时长,超过这个时间要再请求连接一下
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_typs text/css text/xml application/javascript; #指>定压缩的类型 gzip_vary on; #vary header支持; gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript text/css application/xml; gzip_vary on ; #varyheader支持,改选项可以让前端的缓存服务器缓存经过GZIP压缩的页面,例如用Squid缓存经过nginx压缩的数据。
其目的是保护服务器资源,cpu,内存没控制连接数,因为建立连接也是需要消耗资源的。
keepalive_timeout 60; #客户端连接保持会话超时时间,超过这个时间,服务器断开这个链接。 tcp_nodelay on; #也是防止网络阻塞,不过要包涵在keepalived参数才有效。 client_header_buffer_size 4k; #客户端请求头部的缓冲区大小,这个可以根据你的系统分页大小来设置,一般一个请求头的大小不会超过 1k,不过由于一般系统分页都要大于1k,所以这里设置为分页大小。分页大小可以用命令getconf PAGESIZE取得。 open_file_cache max=102400 inactive=20s; #这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive 是指经过多长时间文件没被请求后删除缓存。 open_file_cache_valid 30s; #这个是指多长时间检查一次缓存的有效信息。 open_file_cache_min_uses 1; #open_file_cache指令中的inactive 参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive 时间内一次没被使用,它将被移除。 client_header_timeout 15; #设置请求头的超时时间。我们也可以把这个设置低些,如果超过这个时间没有发送任何数据,nginx将返回request time out的错误。 client_body_timeout 15; #设置请求体的超时时间。我们也可以把这个设置低些,超过这个时间没有发送任何数据,和上面一样的错误提示。 reset_timedout_connection on; #告诉nginx关闭不响应的客户端连接。这将会释放那个客户端所占有的内存空间。 send_timeout 15; #响应客户端超时时间,这个超时时间仅限于两个活动之间的时间,如果超过这个时间,客户端没有任何活动,nginx关闭连接。 server_tokens off; #并不会让nginx执行的速度更快,但它可以关闭在错误页面中的nginx版本数字,这样对于安全性是有好处的。 client_max_body_size 10m; #上传文件大小限制。
proxy_connect_timeout 90; #后端服务器连接的超时时间,发起握手等候响应超时时间 proxy_send_timeout 90; #后端服务器数据回传时间,就是在规定时间内后端服务器必须传完所有的数据 proxy_read_timeout 90; #连接成功后,等候后端服务器响应时间,其实已经进入后端的排队之中等候处理(也可以说是后端服务器处理请求的时间,页面等待服务器响应时间) proxy_buffers 4 32k; #4是数量 32k是大小 该指令设置缓存区的大小和数量,从被代理的后端服务器取得的第一部分响应内容,会放置到这里,默认情况下,一个缓存区的大小等于内存页面大小,可能是4k也可能是8k取决于平台 proxy_busy_buffers_size 64k; #nginx在收到服务器数据后,会分配一部分缓冲区来用于向客户端发送数据,这个缓存区大小由proxy_busy_buffers_size决定的。大小通常是proxy_buffers单位大小的两倍,官网默认是8k/16k。
所有的proxy buffer参数都是作用到每一个请求的。每一个请求会按照参数的配置获得自己的buffer。proxy buffer不是global(全局配置),而是per request(在请求前执行的操作(如时间戳、签名))的
proxy_buffering是为了开启response buffering of the proxied server(反向代理服务器响应数据的缓存),开启后proxy_buffers和proxy_busy_buffers_size参数才会起作用
无论proxy_buffer是否开启,proxy_buffer_size都是工作的,proxy_buffer_size所设置的buffer_size的作用是用来存储upstream端response的header。
在proxy_buffering 开启的情况下,Nginx将会尽可能的读取所有的upstream端传输的数据到buffer,直到proxy_buffers设置的所有buffer们被写满或者数据被读取完(EOF)。此时nginx开始向客户端传输数据,会同时传输这一整串buffer们。同时如果response的内容很大的话,Nginx会接收并把他们写入到temp_file里去。大小由proxy_max_temp_file_size控制。如果busy的buffer传输完了会从temp_file里面接着读数据,直到传输完毕。
一旦proxy_buffers设置的buffer被写入,直到buffer里面的数据被完整的传输完(传输到客户端),这个buffer将会一直处在busy状态,我们不能对这个buffer进行任何别的操作。所有处在busy状态的buffer size加起来不能超过proxy_busy_buffers_size,所以proxy_busy_buffers_size是用来控制同时传输到客户端的buffer数量的。
proxy_set_header用来设定被代理服务器接收到的header信息
语法:proxy_set_header field value;
field :为要更改的项目,也可以理解为变量的名字,比如host
value :为变量的值
如果不设置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; # 代理服务器匹配请求web01服务器的时候,默认加上Host头信息(域名) proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加客户端ip地址以及全链路ip地址到web服务器记录日志 proxy_next_upstream error timeout http_500 http_502 http_503 http_504; # 当Nginx虚拟服务池其中任意一台服务器返回错误码500,502,503,504等错误时,可以分配到下一台服务器程序继续处理,提高平台访问的成功率 proxy_connect_timeout 60s; # nginx代理与后端服务器连接超时时间(代理连接超时) proxy_read_timeout 60s; # nginx代理等待后端服务器的响应时间(代理响应超时) proxy_send_timeout 60s; # 后端服务器数据回传给nginx代理超时时间(回传数据给代理超时) proxy_buffering on; # nginx会把后端返回的内容先放到缓冲区中,然后再返回给客户端,边收边传,不是全部接收再传给客户端 proxy_buffers_size 4k; # 设置nginx代理保存用户头信息的缓存区大小 proxy_buffers 8 8k; # 设置nginx代理缓冲区大小(8*8 = 64k) ... } }
$proxy_host 自然是 proxy_pass后面跟着的host
比如一个请求 https://zhidao.baidu.com/question/22908463.html
Request.ServerVariables(“HTTP_HOST”) 可以获得HTTP_HOST请求标题:zhidao.baidu.com
$http_host始终等于HTTP_HOST请求标题
$host等于$http_host,小写并且没有端口号(如果存在),除非HTTP_HOST不存在或是空值。在这种情况下,$host等于server_name
$proxy_add_x_forwarded_for 从客户访问到到最后的后台服务所经理的所有节点的ip汇总221.221.1.100,10.30.30.109,…
nginx的自带变量 $remote_addr 代表客户端的IP
$remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的web服务器(Nginx,Apache等)就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP。
$remote_addr 只能获取到与服务器本身直连的上层请求ip,所以设置$remote_addr一般都是设置第一个代理上面;但是问题是,有时候是通过cdn访问过来的,那么后面web服务器获取到的,永远都是cdn的ip 而非真是用户ip,那么这个时候就要用到X-Forwarded-For 了,这个变量的意思,其实就像是链路反追踪,从客户的真实ip为起点,穿过多层级的proxy ,最终到达web 服务器,都会记录下来,所以在获取用户真实ip的时候,一般就可以设置成,proxy_set_header X-Forwarded-For
$proxy_add_x_forwarded_for; 这样就能获取所有的代理ip 客户ip $proxy_add_x_forwarded_for变量包含客户端请求头中的X-Forwarded-For与$remote_addr两部分,他们之间用逗号分开。
sendfile on; #开启高效文件传输模式。 tcp_nopush on;#需要在sendfile开启模式才有效,防止网络阻塞,级级的减少网络报文段的数量。将响应头和正文的开始部分一起发送,而不是一个接一个的发送(也就是说数据包不会马上传出等到数据包最大时一次性传) tcp_nodelay on; #只要有数据包产生,不管大小多少,就尽快传输
fastcgi_connect_timeout 600; #指定连接到后端FastCGI的超时时间。 fastcgi_send_timeout 600; #向FastCGI传送请求的超时时间。 fastcgi_read_timeout 600; #指定接收FastCGI应答的超时时间。 fastcgi_buffer_size 64k; #指定读取FastCGI应答第一部分需要用多大的缓冲区,默认的缓冲区大小为。fastcgi_buffers指令中的每块大小,可以将这个值设置更小。 fastcgi_buffers 4 64k; #指定本地需要用多少和多大的缓冲区来缓冲FastCGI的应答请求,如果一个php脚本所产生的页面大小为256KB,那么会分配4个64KB的缓冲区来缓存,如果页面大小大于256KB,那么大于256KB的部分会缓存到fastcgi_temp_path指定的路径中,但是这并不是好方法,因为内存中的数据处理速度要快于磁盘。一般这个值应该为站点中php脚本所产生的页面大小的中间值,如果站点大部分脚本所产生的页面大小为256KB,那么可以把这个值设置为“8 32K”、“4 64k”等。 fastcgi_busy_buffers_size 128k; #建议设置为fastcgi_buffers的两倍,繁忙时候的buffer。 fastcgi_temp_file_write_size 128k; #在写入fastcgi_temp_path时将用多大的数据块,默认值是fastcgi_buffers的两倍,该数值设置小时若负载上来时可能报502BadGateway。 fastcgi_temp_path /usr/local/nginx1.22/nginx_tmp; #缓存临时目录。 fastcgi_intercept_errors on; #这个指令指定是否传递4xx和5xx错误信息到客户端,或者允许nginx使用error_page处理错误信息。注:静态文件不存在会返回404页面,但是php页面则返回空白页! fastcgi_cache_path /usr/local/nginx1.22/fastcgi_cache levels=1:2 keys_zone=cache_fastcgi:128m inactive=1d max_size=10g; #fastcgi_cache缓存目录,可以设置目录层级,比如1:2会生成16*256个子目录,cache_fastcgi是这个缓存空间的名字,cache是用多少内存(这样热门的内容nginx直接放内存,提高访问速度),inactive表示默认失效时间,如果缓存数据在失效时间内没有被访问,将被删除,max_size表示最多用多少硬盘空间。
此外还有
fastcgi_cache cache_fastcgi:#表示开启FastCGI缓存并为其指定一个名称。开启缓存非常有用,可以有效降低CPU的负载,并且防止502的错误放生。cache_fastcgi为proxy_cache_path指令创建的缓存区名称。
fastcgi_cache_valid 200 302 1h:#用来指定应答代码的缓存时间,实例中的值表示将200和302应答缓存一小时,要和fastcgi_cache配合使用。
fastcgi_cache_valid 301 1d:将301应答缓存一天。
fastcgi_cache_valid any 1m:将其他应答缓存为1分钟。
fastcgi_cache_min_uses 1:该指令用于设置经过多少次请求的相同URL将被缓存。
fastcgi_cache_key http://hosthosthostrequest_uri:该指令用来设置web缓存的Key值,nginx根据Key值md5哈希存储.一般根据host (域名) 、host(域名)、host(域名)、request_uri(请求的路径)等变量组合成proxy_cache_key 。
fastcgi_pass:指定FastCGI服务器监听端口与地址,可以是本机或者其它。
nginx的缓存功能有:proxy_cache / fastcgi_cache
proxy_cache的作用是缓存后端服务器的内容,可能是任何内容,包括静态的和动态。
fastcgi_cache的作用是缓存fastcgi生成的内容,很多情况是php生成的动态的内容。
proxy_cache缓存减少了nginx与后端通信的次数,节省了传输时间和后端宽带。
fastcgi_cache缓存减少了nginx与php的通信的次数,更减轻了php和数据库(mysql)的压力。
缓存,主要针对于图片,css,js等元素更改机会比较少的情况下使用,特别是图片,占用带宽大,我们完全可以设置图片在浏览器本地缓存365d,css,js,html可以缓存个10来天,这样用户第一次打开加载慢一点,第二次,就非常快了!缓存的时候,我们需要将需要缓存的拓展名列出来, Expires缓存配置在server字段里面。
location ~* \.(ico|jpe?g|gif|png|jpeg|bmp|swf|flv)$ { expires 30d; #过期时间为30天 #log_not_found off; access_log off; } location ~* \.(js|css)$ { expires 7d; log_not_found off; access_log off; }
注:log_not_found off;是否在error_log中记录不存在的错误。默认是。
Expires:是缓存到期的时间
我们构建网站是为了让用户访问它们,我们希望用于合法访问。所以不得不采取一些措施限制滥用访问的用户。这种滥用指的是从同一IP每秒到服务器请求的连接数。因为这可能是在同一时间内,世界各地的多台机器上的爬虫机器人多次尝试爬取网站的内容。
限速分为很多种限速方法:
1)下载速度限速
2)单位时间内请求数限制
3)基于客户端的并发连接限速
Nginx官方版本限制IP的连接和并发分别有两个模块:
limit_req_zone 用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 “leaky bucket”。
limit_req_conn 用来限制同一时间连接数,即并发限制。
应用场景
下载限速:限制现在速度及并发连接数,应用在下载服务器中,保护带宽及服务器的IO资源。
请求限速:限制单位时间内用户访问请求,防止恶意攻击,保护服务器及资源安全。
算法思想是:
水(请求)从上方倒入水桶,从水桶下方流出(被处理);
来不及流出的水存在水桶中(缓冲),以固定速率流出;
水桶满后水溢出(丢弃)。
这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃。
相比漏桶算法,令牌桶算法不同之处在于它不但有一只“桶”,还有个队列,这个桶是用来存放令牌的,队列才是用来存放请求的。
可用于防止DDoS攻击,或防止上游服务器同时被太多请求淹没。
16000个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,重点说明一下这个配置,burst爆发的意思,这个配置的意思是设置一个大小为5的缓冲区,当有大量请求(爆发)过来时,超过了访问频次限制的请求可以先放到这个缓冲区内等待,但是这个等待区里的位置只有5个,超过的请求会直接报503的错误然后返回
。
第三个参数:nodelay,如果设置,会在瞬时提供处理(burst+rate)个请求的能力
,请求超过(burst+rate)的时候就会直接返回503,永远不存在请求需要等待的情况(这里的rate的单位是:r/s)
如果没有设置,则所有请求会依次等待排队。
burst详解及试验:
设定一个场景,一个nginx反向代理的后端服务单页请求数是30个,如果我们设置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,即每六秒才处理一次请求,如下:
查看当前的tcp连接数
netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
结果如下:
使用ab测试工具,发起10个并发请求:
ab -n 10 -c 10 url
ab工具返回结果:
可以看到一共10个请求,9个请求都失败了。且0.002秒就完成了压测
接着查看当前的tcp连接数:
可以观察到此时服务端的TIME_WAIT 比上图多了10,这意味着是服务端主动要求断开了所有TCP连接
接着再查看 /var/log/nginx/access.log,印证了只有一个请求成功了,其它就是都直接返回了503,即服务器拒绝了请求。
查看当前的tcp连接数
netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
结果如下:
使用ab测试工具,发起10个并发请求:
ab -n 10 -c 10 url
可以看到测试经过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个请求。
查看当前的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个请求位置。
limit_conn_zone 中1MB的内存空间可以存储32000个32B或16000个64B的变量计数状态。
变量计数状态在32位系统平台占用32B或64B,在64位系统平台占用64B
10M的话换算下来可以保留16万个IP地址信息
#基于IP做连接限制 限制同一IP并发为1 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 { limit_conn addr 1; } } }
# 下载速度为100k server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } location /abc { limit_rate 100k; } } }
http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #基于IP做连接限制 limit_conn_zone $binary_remote_addr zone=addr:10m; #基于IP对下载速率做限制,限制每秒处理1次请求, limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } location /abc { # 对突发超过5个以后的请求放入缓存区 limit_req zone=one burst=5 nodelay; # 限制并发连接数为4 limit_conn addr 4; # 限制下载速度为100K limit_rate 100k; } } }
#限制用户连接数来预防DOS攻击 limit_conn_zone $binary_remote_addr zone=perip:10m; limit_conn_zone $server_name zone=perserver:10m; #限制同一客户端ip最大并发连接数 limit_conn perip 2; #限制同一server最大并发连接数 limit_conn perserver 20; #限制下载速度,根据自身服务器带宽配置 limit_rate 300k;
vim /etc/sysctl.conf #进入这个文件修改 sysctl -p #让修改的内核信息生效 ################################################# fs.file-max = 999999 net.ipv4.ip_forward = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 kernel.sysrq = 0 kernel.core_uses_pid = 1 net.ipv4.tcp_syncookies = 1 kernel.msgmnb = 65536 kernel.msgmax = 65536 kernel.shmmax = 68719476736 kernel.shmall = 4294967296 net.ipv4.tcp_max_tw_buckets = 6000 net.ipv4.tcp_sack = 1 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_rmem = 10240 87380 12582912 net.ipv4.tcp_wmem = 10240 87380 12582912 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 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.tcp_max_syn_backlog = 262144 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_synack_retries = 1 net.ipv4.tcp_syn_retries = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_fin_timeout = 1 net.ipv4.tcp_keepalive_time = 30 net.ipv4.ip_local_port_range = 1024 65000
注:主动关闭连接的服务端会产生TIME_WAIT状态的连接
net.core.somaxconn:限制到128,而nginx定义的NGX_LISTEN_BACKLOG 默认为511,所以有必要调整这个值。注:对于一个TCP连接,Server与Client需要通过三次握手来建立网络连接.当三次握手成功后,我们可以看到端口的状态由LISTEN转变为ESTABLISHED,接着这条链路上就可以开始传送数据了.每一个处于监听(Listen)状态的端口,都有自己的监听队列.监听队列的长度与如somaxconn参数和使用该端口的程序中listen()函数有关。somaxconn定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数,默认值为128,对于一个经常处理新连接的高负载 web服务环境来说,默认的 128 太小了。大多数环境这个值建议增加到 1024 或者更多。大的侦听队列对防止拒绝服务 DoS 攻击也会有所帮助。
#如果想把timewait降下了就要把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
worker_processes 1; pid /var/run/nginx.pid; events { worker_connections 2048; multi_accept on; use 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; tcp_nopush on; tcp_nodelay on; 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 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; }
upstream ygoapi{ server ip:port fail_timeout=5 max_fails=3; server ip:port fail_timeout=5 max_fails=3; ip_hash; #负载均衡策略 }
#隐藏版本信息 server_tokens off; server { listen 80; server_name www.ygoclub.com; charset utf-8; #重定向HTTP请求到HTTPS return 301 https://$server_name$request_uri; }
这里主要是配置ssl证书
在nginx服务的目录下创建cert
server { listen 443; server_name 域名; ssl on; root /xxx/xxx/html; // 前台文件存放文件夹,一般使用 Nginx 初始化的文件夹,当然也可以自己修改 index index.html;// 上面配置的文件夹里面的index.html ssl_certificate /路径/证书名称.pem; ssl_certificate_key /路径/证书名称.key; ssl_session_timeout 5m; # SSL协议配置 ssl_protocols SSLv2 SSLv3 TLSv1.2; #表示使用TLS类型协议 ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; #表示使用加密套件的类型 ssl_prefer_server_ciphers on; location / { 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_pass http://127.0.0.1:8080; } } } server { listen 80; server_name your-domain.com;// 你的域名 rewrite ^(.*)$ https://$host:443$1 permanent;// 把http的域名请求转成https且转发到443端口 }
上一篇:Nginx常用配置及代理转发