当我需要进行性能优化时,说明我们服务器无法满足日益增长的业务。性能优化是一个比较大的课题,需要从以下几个方面进行探讨:
- 当前系统结构瓶颈
- 了解业务模式
- 性能与安全
首先需要了解的是当前系统瓶颈,用的是什么,跑的是什么业务。里面的服务是什么样子,每个服务最大支持多少并发。比如针对Nginx而言,我们处理静态资源效率最高的瓶颈是多大?
可以通过查看当前cpu负荷,内存使用率,进程使用率来做简单判断。还可以通过操作系统的一些工具来判断当前系统性能瓶颈,如分析对应的日志,查看请求数量。也可以通过nginx http_stub_status_module模块来查看对应的连接数,总握手次数,总请求数。也可以对线上进行压力测试,来了解当前的系统的性能,并发数,做好性能评估。
虽然我们是在做性能优化,但还是要熟悉业务,最终目的都是为业务服务的。我们要了解每一个接口业务类型是什么样的业务,比如电子商务抢购模式,这种情况平时流量会很小,但是到了抢购时间,流量一下子就会猛涨。也要了解系统层级结构,每一层在中间层做的是代理还是动静分离,还是后台进行直接服务。需要我们对业务接入层和系统层次要有一个梳理
性能与安全也是一个需要考虑的因素,往往大家注重性能忽略安全或注重安全又忽略性能。比如说我们在设计防火墙时,如果规则过于全面肯定会对性能方面有影响。如果对性能过于注重在安全方面肯定会留下很大隐患。所以大家要评估好两者的关系,把握好两者的孰重孰轻,以及整体的相关性。权衡好对应的点。
大家对相关的系统瓶颈及现状有了一定的了解之后,就可以根据影响性能方面做一个全体的评估和优化。
- 网络(网络流量、是否有丢包,网络的稳定性都会影响用户请求)
- 系统(系统负载、饱和、内存使用率、系统的稳定性、硬件磁盘是否有损坏)
- 服务(连接优化、内核性能优化、http服务请求优化都可以在nginx中根据业务来进行设置)
- 程序(接口性能、处理请求速度、每个程序的执行效率)
- 数据库、底层服务
linux/Unix上,一切皆文件,每一次用户发起请求就会生成一个文件句柄,文件句柄可以理解为就是一个索引,所以文件句柄就会随着请求量的增多,而进程调用的频率增加,文件句柄的产生就越多,系统对文件句柄默认的限制是1024个,对Nginx来说非常小了,需要改大一点。不然遇到负载较大的服务器时,很容易遇到error: too many open files。
worker_rlimit_nofile 65535;
这个指令是一个worker进程能打开的最多文件描述符数目(socket连接也是一种句柄),理论值应该是最多打开文件数(ulimit -n)与nginx 进程数相除,但是nginx 分配请求并不是那么均匀,所以最好与ulimit -n 的值保持一致。
如果linux 2.6内核下开启文件打开数为65535,worker_rlimit_nofile就相应应该填写65535。
这是因为nginx调度时分配请求到进程并不是那么的均衡,所以假如填写10240,总并发量达到3-4万时就有进程可能超过10240了,这时会返回502错误。
注:ulimit -n命令
通过ulimit -n命令可以查看linux系统里打开文件描述符的最大值,一般缺省值是1024。
修改ulimit方法:
使用 ulimit -n 65535 可即时修改,但重启后就无效了;
有如下三种修改方式:
1.在/etc/rc.local 中增加一行 ulimit -SHn 65535
2.在/etc/profile 中增加一行 ulimit -SHn 65535
3.在/etc/security/limits.conf最后增加如下记录:(centos使用此方式有效)
- soft nofile 65535
- hard nofile 65535
- soft nproc 65535
- hard nproc 65535
cpu的亲和能够使nginx对于不同的work工作进程绑定到不同的cpu上面去。就能够减少在work间不断切换cpu,把进程通常不会在处理器之间频繁迁移,进程迁移的频率小,来减少性能损耗。
比如8核CPU配置:
#nginx进程数,建议按照cpu 数目来指定,一般为它的倍数 (如,2个四核的cpu计为8)。
#worker_processes最多开启8个,8个以上性能提升不会再提升了,而且稳定性变得更低,所以8个进程够用了。
worker_processes 8;
#为每个进程分配cpu,上例中将8 个进程分配到8 个cpu。
#nginx默认是没有开启利用多核cpu的配置的。需要通过增加worker_cpu_affinity配置参数来充分利用多核cpu,cpu是任务处理,当计算最费时资源的时候,cpu核使用上的越多,性能就越好。
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
在nginx 1.9版本之后,就帮我们自动绑定了cpu,允许将工作进程自动绑定到可用的CPU:
worker_processes auto;
worker_cpu_affinity auto;
注:是否可以通过增加worker_cpu_affinity配置参数来充分利用多核cpu的性能?
如果服务器只配置了worker_processes,在进行压测的时候,各CPU的使用率会同步上升;
worker_processes配置是指定启动多少个nginx进程,linux会自动进行任务分配,所以压力上来后各CPU都会进行负载,所以worker_processes配置肯定是起到了启用多核CPU的作用。
worker_cpu_affinity配置我认为是可以实现更自由的指定CPU分配,可以将进程和CPU核心对应起来。
nginx的连接处理机制在于不同的操作系统会采用不同的I/O模型,Linux下,nginx使用epoll的I/O多路复用模型,在freebsd使用kqueue的IO多路复用模型,在solaris使用/dev/pool方式的IO多路复用模型,在windows使用的icop等等。要根据系统类型不同选择不同的事务处理模型,我们使用的是Centos,因此将nginx的事件处理模型调整为epoll模型。
说明:在不指定事件处理模型时,nginx默认会自动的选择最佳的事件处理模型服务。
事件处理模型配置:
events {
#该指令用来设置nginx事件处理模型
use epoll;
#该指令设置单个worker进程的最大连接数(查看相关资料,生产环境中worker_connections 建议值最好超过9000)
#同时连接的数量受限于系统上可用的文件描述符的数量,因为每个socket将打开一个文件描述符。 如果NGINX尝试打开比可用文件描述符更多的socket,会发现error.log中出现Too many opened files的信息。
worker_connections 65535;
#告诉nginx收到一个新连·接通知后接受尽可能多的连接,默认是on,设置为on后,多个worker按串行方式来处理连接,也就是一个连接只有一个worker被唤醒,其他的处于休眠状态,设置为off后,多个worker按并行方式来处理连接,也就是一个连接会唤醒所有的worker,直到连接分配完毕,没有取得连接的继续休眠。当你的服务器连接数不多时,开启这个参数会让负载有一定的降低,但是当服务器的吞吐量很大时,为了效率,可以关闭这个参数。
multi_accept on;
}
注:针对worker_connections:
客户端正向代理数 = worker_rlimit_nofile / 2 >= worker_connections * worker_process /2
客户端可连反向代理数据 = worker_rlimit_nofile /4 >= worker_connections * worker_process /4
#上下文: http, server, location
keepalive_timeout 60s;
Nginx 使用 keepalive_timeout 来指定 KeepAlive 的超时时间(timeout)。指定每个 TCP 连接最多可以保持多长时间。Nginx 的默认值是 75 秒,有些浏览器最多只保持 60 秒,所以可以设定为 60 秒。若将它设置为 0,就禁止了 keepalive 连接。
#上下文: http, server, location
client_max_body_size 10m;
最大允许上传的文件大小,根据业务需求来设置。如果上传的文件大小超过该设置,那么就会报413 Request Entity Too Large的错误。
#上下文: http, server, location
client_body_timeout 20s;
指定客户端与服务端建立连接后发送 request body 的超时时间。如果客户端在指定时间内没有发送任何内容,Nginx 返回 HTTP 408(Request Timed Out)。
#上下文: http, server, location
client_header_timeout 10s;
客户端向服务端发送一个完整的 request header 的超时时间。如果客户端在指定时间内没有发送一个完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)。
#上下文: http, server, location
send_timeout 30s;
服务端向客户端传输数据的超时时间。
upstream server超时配置:
#上下文: http, server, location
proxy_connect_timeout 60s;
该指令设置与upstream server的连接超时时间,默认值是60秒,这个超时不能超过75秒。
#上下文: http, server, location
proxy_send_timeout 90;
该指令设置nginx发送数据至upstream server超时, 默认60s, 如果连续的60s内没有发送1个字节, 连接关闭
#上下文: http, server, location
proxy_read_timeout 4k;
该指令设置与upstream server的读超时时间。它决定了nginx会等待多长时间来获得请求的响应。默认60s, 如果连续的60s内没有收到1个字节, 连接关闭
#上下文: http, server, location
proxy_buffering on;
#上下文: http, server, location
proxy_buffers 4 32k;
该指令开启从后端被代理服务器的响应内容缓冲。默认开启,缓存可以加快响应速度,但是也消耗内存
该指令设置缓冲区大小,从代理后端服务器取得的第一部分的响应内容,会放到这里
#上下文: http, server, location
proxy_busy_buffers_size 64k
nginx在收到服务器数据后,会分配一部分缓冲区来用于向客户端发送数据。
各种静态资源太大,从服务器获取静态资源很慢。Nginx开启Gzip压缩功能, 可以使网站的css、js 、xml、html 文件在传输时进行压缩,提高访问速度, 进而优化Nginx性能。
Nginx gzip压缩的优点
- 提升网站用户体验:发送给用户的内容小了,用户访问单位大小的页面就加快了,用户体验提升了,网站口碑就好了
- 节约网站带宽成本:数据是压缩传输的,因此节省了网站的带宽流量成本,不过压缩时会稍微消耗一些CPU资源,这个一般可以忽略不计
需要和不需要压缩的对象
纯文本内容压缩比很高,因此,纯文本的内容最好进行压缩,例如:html、css、js、xml、shtml等格式的文件
被压缩的纯文本文件必须大于1KB,由于压缩算法的特殊原因,极小的文件压缩后可能反而变大
图片、视频(流媒体)等文件尽量不要压缩,因为这些文件大多都是经过压缩的,如果再压缩很可能不会减小或减小很少,或者可能增大,同事压缩时还会消耗大量的CPU、内存资源
gzip常用参数说明
server{
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; #压缩比率(1-9), 一般选择4-6,为了性能,越大压缩率越高,同时消耗cpu资源也越多
gzip_types text/css text/xml application/javascript; #指定压缩的类型
gzip_vary on; #vary header支持,该选项可以让前端的缓存服务器缓存经过gzip压缩的页面,例如用Squid缓存经过Nginx压缩的数据
}
以下配置对静态资源的处理比较有效,如果做静态资源服务器可以打开。
传统文件读取方式:硬件—>内核—>用户空间—>程序空间—>程序内核空间—>套接字服务。
开启sendfile服务后:硬件—>内核—>程序内核空间—>套接字服务。
# 开启高效文件传输模式。
sendfile on;
#需要在sendfile开启模式才有效,防止网络阻塞,积极的减少网络报文段的数量,提升nginx的网络传输效率,大文件推荐打开。将响应头和正文的开始部分一起发送,而不一个接一个的发送。
tcp_nopush on;
网页的图片,js ,css ,视频 都加 http accept-ranges头,以支持多线程加载,断点续传,提高性能!目前各大网站都在使用此方式!
expires 30d-------配置文件缓存时间是30天。
server {
listen 80;
server_name p2hp.com;
location ~ ^/(img/|js/|css/|upload/|font/|fonts/|res/|video) {
add_header Access-Control-Allow-Origin *;
add_header Accept-Ranges bytes;
root /var/www/...;
access_log off;
expires 30d;
}
}
一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的。因此,我们应尽量隐藏或者消除Web服务对访问用户显示各类敏感信息(例如Web软件名称以及版本号等信息),增加恶意用户攻击服务器的难度,从而加强Web服务器的安全性。
在nginx.conf中的http标签段内加入server_tokens off;参数,重启服务就可以隐藏nginx版本号:
#隐藏nginx的版本号
server_tokens off;
如果我们自己网站内的图片资源被其它网站所盗用,这会增加自己网站的带宽资源,增加很多额外的消耗,而且会对我们系统的稳定性有影响,为了防止自己网站上的图片资源被其它网站所盗用,我们需要给自己的服务器配置防盗链。
在Nginx的配置文件nginx.conf的 server段匹配图片资源允许的域名,,不匹配的直接重定向到其它连接或者直接返回403错误。
#图片防盗链
location ~* \.(png|jpg|jpeg|gif|swf|flv)$ {
# 指定合法的来源referer(上一个页面的来源的域名),匹配成功(*.test.com)这个变量被设置为0,匹配失败设置为1
valid_referers none blocked *.test.com;
if ($invalid_referer) {
# 如果有盗链的情况就使用url重写到错误页面(示例重定向到了百度首页logo图片)
rewrite ^/ https://www.baidu.com/img/bd_logo1.png?qua=high;
# 或者直接返回403错误码
#return 403;
}
Robots协议(也称为爬虫协议、机器人协议等)的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎那些页面可以抓取,那些页面不能抓取。
Nginx防爬虫优化配置
我们可以根据客户端的user-agent信息,轻松地阻止指定的爬虫爬取我们的网站。下面来看几个案例。
范例:阻止下载协议代理,命令如下:
if ($http_user_agent ~* LWP::Simple|BBBike|wget) {
return 403;
}
说明:如果用户匹配了if后面的客户端(例如wget),就返回403
什么是跨站访问,当我们通过浏览器访问a网站时,同时会利用ajax或其他方式,同时也请求b网站,这样的话就出现了请求一个页面,使用2个域名,这种方式对浏览器来说默认是禁止。
那么Nginx允许跨站访问与浏览器有什么关系呢,因为浏览器会读取Access-Control-Allow-Origin的头信息,如果服务端允许,则浏览器不会进行拦截。
location / {
index index.html index.html;
# 添加header头信息Access-Control-Allow-Origin,*号允许所有的域名跨域访问
add_header Access-Control-Allow-Origin *;
}
作为高性能WEB服务器,只调整Nginx本身的参数是不行的,因为Nginx服务依赖于高性能的操作系统。
Linux性能评估表
以下为常见的几个Linux内核参数优化方法
对于tcp连接,服务端和客户端通信完后状态变为timewait,假如某台服务器非常忙,连接数特别多的话,那么这个timewait数量就会越来越大。
毕竟它也是会占用一定的资源,所以应该有一个最大值,当超过这个值,系统就会删除最早的连接,这样始终保持在一个数量级。
这个数值就是由net.ipv4.tcp_max_tw_buckets这个参数来决定的。
CentOS7系统,你可以使用sysctl -a |grep tw_buckets来查看它的值,默认为32768,
你可以适当把它调低,比如调整到8000,毕竟这个状态的连接太多也是会消耗资源的。但你不要把它调到几十、几百这样,因为这种状态的tcp连接也是有用的,如果同样的客户端再次和服务端通信,就不用再次建立新的连接了,用这个旧的通道,省时省力。
该参数的作用是快速回收timewait状态的连接。上面虽然提到系统会自动删除掉timewait状态的连接,但如果把这样的连接重新利用起来岂不是更好。
所以该参数设置为1就可以让timewait状态的连接快速回收,它需要和下面的参数配合一起使用。
开启重用。该参数设置为1,将timewait状态的连接重新用于新的TCP连接,要结合上面的参数一起使用。
tcp三次握手中,客户端向服务端发起syn请求,服务端收到后,也会向客户端发起syn请求同时连带ack确认,假如客户端发送请求后直接断开和服务端的连接,不接收服务端发起的这个请求,服务端会重试多次,这个重试的过程会持续一段时间(通常高于30s),当这种状态的连接数量非常大时,服务器会消耗很大的资源,从而造成瘫痪,正常的连接进不来,这种恶意的半连接行为其实叫做syn flood攻击。
设置为1,是开启SYN Cookies,开启后可以避免发生上述的syn flood攻击。
开启该参数后,服务端接收客户端的ack后,再向客户端发送ack+syn之前会要求client在短时间内回应一个序号,如果客户端不能提供序号或者提供的序号不对则认为该客户端不合法,于是不会发ack+syn给客户端,更涉及不到重试。
该参数定义系统能接受的最大半连接状态的tcp连接数。客户端向服务端发送了syn包,服务端收到后,会记录一下,该参数决定最多能记录几个这样的连接。在CentOS7,默认是256,当有syn flood攻击时,这个数值太小则很容易导致服务器瘫痪,
实际上此时服务器并没有消耗太多资源(cpu、内存等),所以可以适当调大它,比如调整到30000。
该参数适用于客户端,它定义发起syn的最大重试次数,默认为6,建议改为2。
该参数适用于服务端,它定义发起syn+ack的最大重试次数,默认为5,建议改为2,可以适当预防syn flood攻击。
该参数定义端口范围,系统默认保留端口为1024及以下,以上部分为自定义端口。这个参数适用于客户端,当客户端和服务端建立连接时,比如说访问服务端的80端口,客户端随机开启了一个端口和服务端发起连接,
这个参数定义随机端口的范围。默认为32768 60999,建议调整为1025 60999。
如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2 状态的时间。对端可以出错并永远不关闭连接,甚至意外宕机,导致本端连接超时,超时时间缺省值是60 秒。2.2 内核的通常值是180 秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB 服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2 的危险性比FIN-WAIT-1 要小,因为它最多只能吃掉1.5K 内存,但是它们的生存期长些。建议调整为6。
tcp连接状态里,有一个是established状态,只有在这个状态下,客户端和服务端才能通信。正常情况下,当通信完毕,客户端或服务端会告诉对方要关闭连接,此时状态就会变为timewait,如果客户端没有告诉服务端,并且服务端也没有告诉客户端关闭的话(例如,客户端那边断网了),此时需要该参数来判定。比如客户端已经断网了,但服务端上本次连接的状态依然是established,服务端为了确认客户端是否断网,就需要每隔一段时间去发一个探测包去确认一下看看对方是否在线。这个时间就由该参数决定。它的默认值为7200秒,建议设置为600秒。
该参数和上面的参数是一起的,服务端在规定时间内发起了探测,查看客户端是否在线,如果客户端并没有确认,此时服务端还不能认定为对方不在线,而是要尝试多次。该参数定义重新发送探测的时间,即第一次发现对方有问题后,过多久再次发起探测。默认值为75秒,可以改为15秒。
第10和第11个参数规定了何时发起探测和探测失败后再过多久再发起探测,但并没有定义一共探测几次才算结束。
该参数定义发起探测的包的数量。默认为9,建议设置3。
用来限制监听(LISTEN)队列最大数据包的数量,超过这个数量就会导致链接超时或者触发重传机制。
web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而nginx定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值。对繁忙的服务器,增加该值有助于网络性能。
每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。默认值是1024,对重负载服务器而言,该值需要调高一点。
系统所能处理不属于任何进程的TCP sockets最大数量。假如超过这个数量﹐那么不属于任何进程的连接会被立即reset,并同时显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要依赖这个或是人为的降低这个限制。如果内存大更应该增加这个值。系统默认是8092,可以修改为32768。
Timestamps 用在其它一些东西中﹐可以防范那些伪造的sequence 号码。一条1G的宽带线路或许会重遇到带 out-of-line数值的旧sequence 号码(假如它是由于上次产生的)。Timestamp 会让它知道这是个 ‘旧封包’。(该文件表示是否启用以一种比超时重发更精确的方法(RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。)
在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试。默认值为15,根据RTO的值来决定,相当于13-30分钟(RFC1122规定,必须大于100秒).(这个值根据目前的网络设置,可以适当地改小,可以修改为5)
在Linux下调整内核参数,可以直接编辑配置文件/etc/sysctl.conf,然后执行sysctl -p命令生效
net.ipv4.tcp_fin_timeout = 6
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_max_tw_buckets = 8000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 30000
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_synack_retries = 2
net.ipv4.ip_local_port_range = 1025 61000
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 3
net.core.somaxconn = 16384
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_max_orphans = 32768
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_retries2 = 5
Linux内核参数调优
Nginx 性能优化 超详细!!!
Nginx 性能优化(吐血总结)
Nginx安全优化与性能调优
Nginx优化详解(超详细)
Nginx配置多核CPU的配置项worker_cpu_affinity使用方法
3、描述nginx中worker_processes、worker_cpu_affinity、worker_rlimit_nofile、worker_connections配置项的含义
Nginx开启Gzip详解
详解Nginx的超时keeplive_timeout配置步骤