nginx性能优化
考虑的方面
当前系统的瓶颈
- 服务类型结构特点
- 指标
- 系统
- CPU
- MEM
- IO
- 服务
- 请求
- 日志分析统计
- 系统
- 压力测试
- ab接口压力测试
了解业务类型
- 业务接口类型
- 业务系统层次结构
- 代理
- 中间件
- 服务层
性能与安全
监听文件描述符的机制
select
每个socket(fd)对应一个connection
sequenceDiagram connection->>select:request loop 状态查询 select->>socket:ready? alt not ready socket->>select: continue else ready socket->>select: 从用户态复制FD到内核态,并遍历 select->>connection: process end end userspace->>kernelspace:copy_from_user复制fd_set到内核 loop 注册回 kernelspace->>kernelspace:注册回调函数__pollwait kernelspace->>kernelspace:遍历fd_set,调用回调函数,放入队列中 kernelspace->>kernelspace:消息完成后会激活队列相应的进程 alt 将返回的事件消息作为fd的值mask kernelspace->>kernelspace: {key:fd mask:EVEN} else no mask kernelspace->>kernelspace: schudle_timeout end kernelspace-->>userspace:复制fd_set回用户态 end
poll
- 类似select
- 使用链表,无最大连接数限制(由fd_set变成pollfd链表)
epoll
一个文件描述符FD
连接事件复制一次到内核事件表
无连接数限制
使用水平及边缘触发,非轮询,效率高,遍历就绪链表
使用mmap文件映射,内核消息传递减少开销.
sequenceDiagram event->>handler:callback
并发(在可接受的队列处理时间内的最大请求)->并行(多核并行[OS层]或单核分时复用[事件驱动])->异步
- 异步框架(一个创建接口,一个主循环,一个回调)
NGINX
- 网络
- 操作系统
- 文件句柄
- 全局 /etc/security/limits.conf: hard/soft nofile
- 用户 /etc/security/limits.conf: hard/soft nofile
- nginx进程: worker_rlimit_nofile: 35535
- CPU亲和(affinity)
- 减少处理器迁移进程切换消耗cpu
- worker_cpu_affinity 0001 0010 0100 1000或auto或1010 0101
- 减少处理器迁移进程切换消耗cpu
- 文件句柄
- nginx服务
- 配置优化
- 全局优化
- worker_processes number auto;worker进程的数量;通常应该等于小于当前主机的cpu的物理核心数; auto:当前主机物理CPU核心数;
- worker_cpu_affinity auto;
- wrker_priority number [-20, 20]
- worker_rlimit_nofile number; worker进程所能够打开的文件数量上限;
- http
- charset utf-8;
- access_log off;不必要的情况下可关闭日志.
- sendfile on; 提高 Nginx 静态资源托管效率。sendfile 是一个系统调用,直接在内核空间完成文件发送,不需要先 read 再 write,没有上下文切换开销。
- tcp_nopush on; TCP_NOPUSH 是 FreeBSD 的一个 socket 选项,对应 Linux 的 TCP_CORK,Nginx 里统一用
tcp_nopush
来控制它,并且只有在启用了 sendfile 之后才生效。启用它之后,数据包会累计到一定大小之后才会发送,减小了额外开销,提高网络效率。 - tcp_nodeny on;TCP_NODELAY 也是一个 socket 选项,启用后会禁用 Nagle 算法,尽快发送数据,某些情况下可以节约 200ms(Nagle 算法原理是:在发出去的数据还未被确认之前,新生成的小数据先存起来,凑满一个 MSS 或者等到收到确认后再发送)。Nginx 只会针对处于 keep-alive 状态的 TCP 连接才会启用
tcp_nodelay
。可以看到 TCP_NOPUSH 是要等数据包累积到一定大小才发送,TCP_NODELAY 是要尽快发送,二者相互矛盾。实际上,它们确实可以一起用,最终的效果是先填满包,再尽快发送。 - keepalive-timeout 65;指定服务端为每个 TCP 连接最多可以保持多长时间Nginx 的默认值是 75 秒,有些浏览器最多只保持 60 秒
- gzip on;
- 全局优化
- 配置优化
- 后端程序
- 数据库
相同servername顺序
配置文件先读取原则
location正则匹配
http://seanlook.com/2015/05/17/nginx-location-rewrite/
1 | 优先级最高的两个匹配,匹配后不再搜索 |
alias,root
1 | root+location |
错误码
413->client_max_body_szie
502->后端服务无响应
504->后端服务执行超时
nginx rewrite
1 | # 跳转型,浏览器对新地址重新进行请求 |
nginx try_files
动静分离,路径判断,重定向
1 | try_files指令: |
防盗链
1 | Nginx设置Referer来防止盗图 |
nginx信号:
- TERM,INT快速关闭
- HUP 平滑重启,加载配置(重启旧进程)
- USR1 重新打开日志,切日志
- USR2 平滑升级
- -> WINCH 从容关闭worker
- -> QUIT 从容关闭master
config:
worker_rlimit_nofile 51200;
worker_connections 51200;
1 | [root@centos-server modules]# rpm -ql nginx |
1 | [root@centos-server nginx]# nginx -V |
Nginx 监控
1 | # netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' |
上面是一个实际网站的配置实例,其中灰色文字为配置说明。上述配置中,首先我们定义了一个 location ~ ^/NginxStatus/,这样通过 http://localhost/NginxStatus/ 就可以监控到 Nginx 的运行信息,显示的内容如下:
Active connections: 70
server accepts handled requests
14553819 14553819 19239266
Reading: 0 Writing: 3 Waiting: 67
NginxStatus 显示的内容意思如下:
active connections – 当前 Nginx 正处理的活动连接数。
server accepts handled requests -- 总共处理了 14553819 个连接 , 成功创建 14553819 次握手 ( 证明中间没有失败的 ), 总共处理了 19239266 个请求 ( 平均每次握手处理了 1.3 个数据请求 )。
reading -- nginx 读取到客户端的 Header 信息数。
writing -- nginx 返回给客户端的 Header 信息数。
waiting -- 开启 keep-alive 的情况下,这个值等于 active - (reading + writing),意思就是 Nginx 已经处理完正在等候下一次请求指令的驻留连接。
1 | procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu----- |
worker_cpu_affinity配置是写在/etc/nginx/nginx.conf里面的。
2核是 01,四核是0001,8核是00000001,有多少个核,就有几位数,1表示该内核开启,0表示该内核关闭。
- 8核CPU,开户8个进程
worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
0001表示启用第一个CPU内核,0010表示启用第二个CPU内核,依此类推
worker_processes最多开启8个,8个以上性能提升不会再提升了,而且稳定性变得更低,所以8个进程够用了。
优化TCP/IP连接,减少TIME-WAIT的命令
(一)TCP/IP连接的状态和对应的个数:
1 | netstat -an | awk '/^tcp/ {++s[$NF]} END {for(a in s) print a, s[a]}' |
(二)提升服务器的负载能力:
1 | # vim /etc/sysctl.conf |
参数说明:{
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭;
net.ipv4.tcp_fin_timeout 修改系统默认的 TIMEOUT 时间。
}
(三)优化TCP/IP的可使用端口范围,进一步提升服务器的并发能力(针对tcp流量比较大的服务器)
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 5000
参数说明:{
net.ipv4.tcp_keepalive_time = 1200 表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.ip_local_port_range = 10000 65000 表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为10000到65000。(注意:这里不要将最低值设的太低,否则可能会占用掉正常的端口!)
net.ipv4.tcp_max_syn_backlog = 8192 表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_tw_buckets = 5000 表示系统同时保持TIME_WAIT的最大数量,如果超过这个数字,TIME_WAIT将立刻被清除并打印警告信息。默认为180000,改为5000。对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,但是对于 Squid,效果却不大。此项参数可以控制TIME_WAIT的最大数量,避免Squid服务器被大量的TIME_WAIT拖死。
}
(四)修改 linux kernel 的 tcp time wait的时间(适用于大量短连接的情况)
在 $KERNEL/include/net/tcp.h里面,有下面的行:
#define TCP_TIMEWAIT_LEN (60HZ) / how long to wait to destroy TIME-WAIT
* state, about 60 seconds */
而这个宏是真正控制 TCP TIME_WAIT 状态的超时时间的。如果我们希望减少 TIME_WAIT 状态的数目(从而节省一点点内核操作时间),那么可以把这个数值设置低一些,根据我们的测试,设置为 10 秒比较合适,也就是把上面的修改为:
#define TCP_TIMEWAIT_LEN (10*HZ) /* how long to wait to destroy TIME-WAIT
* state, about 60 seconds */
然后重新编译内核,重启系统即可发现短连接造成的TIME_WAIT状态大大减少:
netstat -ant | grep -i time_wait |wc -l
一般情况都可以至少减少2/3。也能相应提高系统应对短连接的速度
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 5
参数说明:{
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭;
net.ipv4.tcp_fin_timeout 修改系统默认的 TIMEOUT 时间。
}
Nginx做web服务器linux内核参数优化
Nginx提供web服务时Linux内核参数调整是必不可少的,其中在优化方面就需要我们格外的注意。在下面就是对Linux内核参数优化的详细介绍,希望大家有所收获。
关于Linux内核参数的优化:
net.ipv4.tcp_max_tw_buckets = 6000
timewait的数量,默认是180000。
net.ipv4.ip_local_port_range = 1024 65000
允许系统打开的端口范围。
net.ipv4.tcp_tw_recycle = 1
启用timewait快速回收。
net.ipv4.tcp_tw_reuse = 1
开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接。
net.ipv4.tcp_syncookies = 1
开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies来处理。
net.core.somaxconn = 262144
web应用中listen函数的backlog默认会给我们内核参数的net.core.somaxconn限制到128,而Nginx内核参数定义的NGX_LISTEN_BACKLOG默认为511,所以有必要调整这个值。
net.core.netdev_max_backlog = 262144
每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。
net.ipv4.tcp_max_orphans = 262144
系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤儿连接将即刻被复位并打印出警告信息。这个限制仅仅是为了防止简单的DoS攻击,不能过分依靠它或者人为地减小这个值,更应该增加这个值(如果增加了内存之后)。
net.ipv4.tcp_max_syn_backlog = 262144
记录的那些尚未收到客户端确认信息的连接请求的最大值。对于有128M内存的系统而言,缺省值是1024,小内存的系统则是128。
net.ipv4.tcp_timestamps = 0
时间戳可以避免序列号的卷绕。一个1Gbps的链路肯定会遇到以前用过的序列号。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉。
net.ipv4.tcp_synack_retries = 1
为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发送SYN+ACK包的数量。
net.ipv4.tcp_syn_retries = 1
在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.tcp_fin_timeout = 1
如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是60秒。2.2 内核的通常值是180秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB服务器,也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2的危险性比FIN-WAIT-1要小,因为它最多只能吃掉1.5K内存,但是它们的生存期长些。
net.ipv4.tcp_keepalive_time = 30
当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时。
1 | [root@192-168-171-146 conf]# ss -s |
nginx 启用SSL
1 | --with-http_ssl_module # This module requires the OpenSSL library |
在https页面中包含以http方式引入的图片、js等资源时,浏览器为了安全起见会阻止加载。
1 | server { |