nginx优化

nginx性能优化

  1. 考虑的方面

    1. 当前系统的瓶颈

      1. 服务类型结构特点
      2. 指标
        1. 系统
          1. CPU
          2. MEM
          3. IO
        2. 服务
          1. 请求
          2. 日志分析统计
      3. 压力测试
        1. ab接口压力测试
    2. 了解业务类型

      1. 业务接口类型
      2. 业务系统层次结构
        1. 代理
        2. 中间件
        3. 服务层
    3. 性能与安全

      1. 监听文件描述符的机制

        1. select

          1. 每个socket(fd)对应一个connection

          2.                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
        2. poll

          1. 类似select
          2. 使用链表,无最大连接数限制(由fd_set变成pollfd链表)
        3. epoll

          1. 一个文件描述符FD

          2. 连接事件复制一次到内核事件表

          3. 无连接数限制

          4. 使用水平及边缘触发,非轮询,效率高,遍历就绪链表

          5. 使用mmap文件映射,内核消息传递减少开销.

          6.                sequenceDiagram
            event->>handler:callback
          7. 并发(在可接受的队列处理时间内的最大请求)->并行(多核并行[OS层]或单核分时复用[事件驱动])->异步

            1. 异步框架(一个创建接口,一个主循环,一个回调)
  2. NGINX

    1. 网络
    2. 操作系统
      1. 文件句柄
        1. 全局 /etc/security/limits.conf: hard/soft nofile
        2. 用户 /etc/security/limits.conf: hard/soft nofile
        3. nginx进程: worker_rlimit_nofile: 35535
      2. CPU亲和(affinity)
        1. 减少处理器迁移进程切换消耗cpu
          1. worker_cpu_affinity 0001 0010 0100 1000或auto或1010 0101
    3. nginx服务
      1. 配置优化
        1. 全局优化
          1. worker_processes number auto;worker进程的数量;通常应该等于小于当前主机的cpu的物理核心数; auto:当前主机物理CPU核心数;
          2. worker_cpu_affinity auto;
          3. wrker_priority number [-20, 20]
          4. worker_rlimit_nofile number; worker进程所能够打开的文件数量上限;
        2. http
          1. charset utf-8;
          2. access_log off;不必要的情况下可关闭日志.
          3. sendfile on; 提高 Nginx 静态资源托管效率。sendfile 是一个系统调用,直接在内核空间完成文件发送,不需要先 read 再 write,没有上下文切换开销。
          4. tcp_nopush on; TCP_NOPUSH 是 FreeBSD 的一个 socket 选项,对应 Linux 的 TCP_CORK,Nginx 里统一用 tcp_nopush 来控制它,并且只有在启用了 sendfile 之后才生效。启用它之后,数据包会累计到一定大小之后才会发送,减小了额外开销,提高网络效率。
          5. tcp_nodeny on;TCP_NODELAY 也是一个 socket 选项,启用后会禁用 Nagle 算法,尽快发送数据,某些情况下可以节约 200ms(Nagle 算法原理是:在发出去的数据还未被确认之前,新生成的小数据先存起来,凑满一个 MSS 或者等到收到确认后再发送)。Nginx 只会针对处于 keep-alive 状态的 TCP 连接才会启用 tcp_nodelay。可以看到 TCP_NOPUSH 是要等数据包累积到一定大小才发送,TCP_NODELAY 是要尽快发送,二者相互矛盾。实际上,它们确实可以一起用,最终的效果是先填满包,再尽快发送。
          6. keepalive-timeout 65;指定服务端为每个 TCP 连接最多可以保持多长时间Nginx 的默认值是 75 秒,有些浏览器最多只保持 60 秒
          7. gzip on;
    4. 后端程序
    5. 数据库

相同servername顺序

配置文件先读取原则

location正则匹配

http://seanlook.com/2015/05/17/nginx-location-rewrite/

1
2
3
4
5
6
7
优先级最高的两个匹配,匹配后不再搜索
=
^~
正则匹配/无正则前辍匹配,匹配后还要向下搜索
~
~*
/image

alias,root

1
2
root+location
alias替换location

错误码

413->client_max_body_szie

502->后端服务无响应

504->后端服务执行超时

nginx rewrite

1
2
3
4
5
6
# 跳转型,浏览器对新地址重新进行请求
redirect:302跳转到rewrtie后面的地址。
permanent:301永久调整到rewrtie后面的地址,即当前地址已经永久迁移到新地址,一般是为了对搜索引擎友好。
# 代理型,服务器内部实现跳转,浏览器无需再次发起请求
last:将rewrite后的地址重新在server标签执行。
break:将rewrite后地址重新在当前的location标签执行。

nginx try_files

动静分离,路径判断,重定向

1
2
3
4
try_files指令:
语法:try_files file ... uri 或 try_files file ... = code
默认值:无
作用域:server location

防盗链

1
2
3
4
5
6
Nginx设置Referer来防止盗图
valid_referers none blocked server_names;

if ($invalid_referer) {
return 403;
}

nginx信号:

  1. TERM,INT快速关闭
  2. HUP 平滑重启,加载配置(重启旧进程)
  3. USR1 重新打开日志,切日志
  4. USR2 平滑升级
    1. -> WINCH 从容关闭worker
    2. -> QUIT 从容关闭master

config:
worker_rlimit_nofile 51200;
worker_connections 51200;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
[root@centos-server modules]# rpm -ql nginx
/etc/logrotate.d/nginx
/etc/nginx
/etc/nginx/conf.d
/etc/nginx/conf.d/default.conf
/etc/nginx/fastcgi_params
/etc/nginx/koi-utf
/etc/nginx/koi-win
/etc/nginx/mime.types
/etc/nginx/modules
/etc/nginx/nginx.conf
/etc/nginx/scgi_params
/etc/nginx/uwsgi_params
/etc/nginx/win-utf
/etc/sysconfig/nginx
/etc/sysconfig/nginx-debug
/usr/lib/systemd/system/nginx-debug.service
/usr/lib/systemd/system/nginx.service
/usr/lib64/nginx
/usr/lib64/nginx/modules
/usr/libexec/initscripts/legacy-actions/nginx
/usr/libexec/initscripts/legacy-actions/nginx/check-reload
/usr/libexec/initscripts/legacy-actions/nginx/upgrade
/usr/sbin/nginx
/usr/sbin/nginx-debug
/usr/share/doc/nginx-1.16.0
/usr/share/doc/nginx-1.16.0/COPYRIGHT
/usr/share/man/man8/nginx.8.gz
/usr/share/nginx
/usr/share/nginx/html
/usr/share/nginx/html/50x.html
/usr/share/nginx/html/index.html
/var/cache/nginx
/var/log/nginx
1
2
3
4
5
6
[root@centos-server nginx]# nginx -V
nginx version: nginx/1.16.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module--with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 22720 2010244 169688 2594920 0 0 0 0 33433 16822 2 21 77 0 0
2 0 22720 2010492 169688 2594952 0 0 0 412 34205 17057 3 21 76 0 0
1 0 22720 2010864 169688 2593692 0 0 0 0 33576 16732 3 21 77 0 0
1 0 22720 2009624 169696 2595632 0 0 0 404 33831 16805 3 20 77 0 0
1 0 22720 2009500 169696 2594784 0 0 0 0 33843 16680 3 21 77 0 0
[root@192-168-171-146 nginx]# vmstat 5
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
0 0 22420 2207684 70400 2497512 0 0 7 56 2 1 1 5 93 0 0
1 0 22420 2205872 70416 2500324 0 0 0 130 31450 15395 2 7 92 0 0
1 0 22420 2203248 70432 2504292 0 0 0 110 31637 15802 2 7 91 0 0
0 0 22420 2202296 70448 2506236 0 0 0 134 31459 15605 2 7 91 0 0
0 0 22420 2202064 70460 2505480 0 0 0 447 31511 15458 2 7 91 1 0

worker_cpu_affinity配置是写在/etc/nginx/nginx.conf里面的。

2核是 01,四核是0001,8核是00000001,有多少个核,就有几位数,1表示该内核开启,0表示该内核关闭。

  1. 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
2
3
4
5
6
# vim /etc/sysctl.conf

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 时间。
}
(三)优化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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@192-168-171-146 conf]# ss -s
Total: 12814 (kernel 12920)
TCP: 16857 (estab 12572, closed 4096, orphaned 153, synrecv 0, timewait 4096/0), ports 1300

Transport Total IP IPv6

- 12920 - -
RAW 0 0 0
UDP 9 6 3
TCP 12761 12757 4
INET 12770 12763 7
FRAG 0 0 0

[root@192-168-171-146 conf]# ss -s
Total: 44751 (kernel 44980)
TCP: 48780 (estab 44533, closed 4096, orphaned 139, synrecv 0, timewait 4096/0), ports 2188

Transport Total IP IPv6

- 44980 - -
RAW 0 0 0
UDP 9 6 3
TCP 44684 44680 4
INET 44693 44686 7
FRAG 0 0 0

nginx 启用SSL

1
--with-http_ssl_module  # This module requires the OpenSSL library

在https页面中包含以http方式引入的图片、js等资源时,浏览器为了安全起见会阻止加载。

1
2
3
4
5
6
7
8
9
10
11
server {
#侦听443端口,这个是ssl访问端口
listen 443 ssl;
# ssl on; 1.15版本这个参数已经废弃,使用listen ssl代替
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
ssl_certificate /usr/local/nginx/conf/cert.pem;
ssl_certificate_key /usr/local/nginx/conf/cert.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
坚持原创技术分享,您的支持将鼓励我继续创作!