NGINX:漫谈优化总结

无情 阅读:264 2021-05-02 13:53:06 评论:0

优化那些事儿

生产环境下网站做前期的优化肯定是比不可少的,简单来说就是用同等条件的硬件资源,处理更多的网站业务,大程度提供网站业务处理能力;前辈留下的实战经验可都是财富,好多坑只有踩过才知道痛,下面就来漫谈下优化的那些事儿

修改系统Ulimit

网站优化,首先要做的是先对系统进行优化,上线的服务器经常是用Linux系统做的;而在Linux下面部署应用的时候,有时候会遇上Socket/File: Can’t open so many files的问题;这是因为Linux是有文件句柄限制的,而且Linux默认不是很高,一般都是1024,这个值也会影响服务器的最大并发数(每个连接都是打开一个文件描述符),生产服务器这个值肯定是不够的;先做个测试看看ulimit的重要性

查看ulimit值:

[root@localhost ~]# ulimit -a 
core file size          (blocks, -c) 0 
data seg size           (kbytes, -d) unlimited 
scheduling priority             (-e) 0 
file size               (blocks, -f) unlimited 
pending signals                 (-i) 7671 
max locked memory       (kbytes, -l) 64 
max memory size         (kbytes, -m) unlimited 
open files                      (-n) 1024 
pipe size            (512 bytes, -p) 8 
POSIX message queues     (bytes, -q) 819200 
real-time priority              (-r) 0 
stack size              (kbytes, -s) 10240 
cpu time               (seconds, -t) unlimited 
max user processes              (-u) 7671 
virtual memory          (kbytes, -v) unlimited 
file locks                      (-x) unlimited 

其中 "open files (-n) 1024 "是Linux操作系统对一个进程打开的文件句柄数量的限制(也包含打开的socket数量,可影响所有底层用到socket的服务,包括MySQL等)

进行ab压力测试:

[root@bogon ~]# ab -n 2000 -c 1000 http://192.168.20.219/paperindex.htm 
 
#发起1000个并发连接,共2000次请求

ab工具下载-》》windows:IT虾米网  Linux:yum -y install httpd  默认在/usr/bin/ab下

统计日志:

[root@localhost logs]# cat host.access.log | wc -l 
2001 
[root@localhost logs]# awk '{print $4}' host.access.log | grep 500 | wc -l 
874 
[root@localhost logs]# awk '{print $4}' host.access.log | grep 200 | wc -l 
1127

web端一共收到2000次请求,200状态码的正常访问1127次,其余800多次客户端访问抛出500异常,未能提供正常响应

修改ulimit:

ulimit -n 65535 暂时生效      ##也可为655360 
若要令修改ulimits的数值永久生效,则必须修改配置文档,可以给ulimit修改命令放入/etc/profile里面,这个方法实在是不方便 
 
还有一个方法是修改/etc/security/limits.conf 
[root@localhost ~]# vim /etc/security/limits.conf  
文件里面有很详细的注释,比如 
* soft nofile 65535 
 
* hard nofile 65535 
星号代表全局, soft为软件,hard为硬件,nofile为这里指可打开文件数。 
把以上两行内容加到 limits.conf文件中即可 
 
另外,要使 limits.conf 文件配置生效,必须要确保 pam_limits.so 文件被加入到启动文件中。查看 /etc/pam.d/login 文件中有: 
[root@localhost ~]# vim /etc/pam.d/login 
session    required     /lib/security/pam_limits.so 
 
修改完重新登录就可以见到效果,可以通过 ulimit -n 查看 重启nginx服务 
[root@localhost ~]# ulimit -n 
65535

重新做压力测试:

[root@bogon ~]# ab -n 100000 -c 10000 http://192.168.20.219/paperindex.htm 
 
Benchmarking 192.168.20.219 (be patient) 
Completed 10000 requests 
Completed 20000 requests 
Completed 30000 requests 
Completed 40000 requests 
Completed 50000 requests 
Completed 60000 requests 
Completed 70000 requests 
Completed 80000 requests 
Completed 90000 requests 
Completed 100000 requests 
Finished 100000 requests 

直接发送100000次请求,10000并发

统计日志:

[root@localhost logs]# cat host.access.log |wc -l 
101337 
[root@localhost logs]# awk '{print $4}' host.access.log |grep 200 |wc -l 
101337

web端一共收到101337次请求,所有请求状态200正常

限制访问连接

生产环境下很多情况是不对访问做限制,但也有特殊状况,比如部署个网络投票评选平台,会有恶意刷票的情况出现,刷票会占用大量系统资源,影响其他正常的访问,这时就需要对访问的IP进行并发数或者访问频率进行限制

nginx有两个模块可以控制访问

  • limit_conn_zone   限制同时并发访问的数量
  • limit_req_zone     限制访问数据,每秒内最多几个请求

但具体有什么不同呢?下面是 nginx 官网上抛出的解释

limit_conn_zone 
Limit simultaneous connections from a client.  
This module makes it possible to limit the number of simultaneous connections for the assigned session or as a special case, from one address.  
 
limit_req_zone 
Limit frequency of connections from a client.  
This module allows you to limit the number of requests for a given session, or as a special case, with one address. 
Restriction done using leaky bucket. 

按照字面的理解

  • limit_conn_zone 功能是限制一个客户端的并发连接数(这个模块可以限制单个地址 的指定会话 或者特殊情况的并发连接数)
  • lit_req_zone的功能是通过漏桶原理来限制用户的连接频率(这个模块允许你去限制单个地址 指定会话或特殊需要 的请求数 )

一个是限制并发连接一个是限制连接频率,表面上似乎看不出来有什么区别,那就直接看效果如何

测试limit_conn_zone修改配置文件:

http { 
    include       mime.types; 
    default_type  application/octet-stream; 
 
    limit_conn_zone $remote_addr zone=James_conn:10m ; 
    
    limit_conn James_conn 1; 
 
    .............. 
}

limit_zone James_conn  $binary_remote_addr  10m;这里的James_conn是声明一个 limit_zone 的名字,$remote_addr是客户端的IP,10m是会话状态储存的空间,1M大概可以保存16000IP
limit_conn James_conn 1 ;限制客户端并发连接数量为1

并发测试:

[root@bogon ~]# ab -n 20 -c 10 http://192.168.20.219/paperindex.htm 
 
#10个并发,20个请求

log日志:

分析日志20次的请求中不只一次返回200,多测试几次依然如此,看来此模块也不一定能限制的住1秒钟1个并发连接,但还是拒绝了大多数的访问

后续:

又对服务器进行了10000并发10000次请求

[root@bogon ~]# ab -n 10000 -c 10000 http://192.168.20.219/paperindex.htm 

日志结果:

短时间内大的并发量处理还是有效果的 

测试limit_req_zone修改配置文件:

http { 
    include       mime.types; 
    default_type  application/octet-stream; 
 
    limit_req_zone  $remote_addr  zone=req_James:10m rate=1r/s; 
 
    ...................................... 
    server { 
        listen       80; 
        server_name  localhost; 
        limit_req   zone=req_James  burst=120 nodelay; 
 
        ........................... 
        } 
} 

简单说明一下, rate=1r/s 的意思是每个地址每秒只能请求一次,也就是说根据漏桶原理 burst=120 一共有120块令牌,并且每秒钟只新增1块牌,120块令牌发完后就不会再响应 ,加上nodelay之后超过 burst大小的请求就会直接返回503,而不是在后台进行排队等待
连接频率测试:

[root@bogon ~]# ab -n 10000 -c 1000 http://192.168.20.219/paperindex.htm 
 
#1000并发的10000次请求 

整个测试过程大概花了4秒钟,也就在一共可以正常的访问124次左右

log日志:

几乎全是503错误,再对日志进行统计

126次正常访问,9949次503错误,结果跟我们猜想的清楚差不多,说明这个模块真的还蛮好用的

番外:根据真实IP做限制

##取用户的真实IP 
 
 map $http_x_forwarded_for  $clientRealIp { 
        ## 没有通过代理,直接用 remote_addr 
        ""      $remote_addr; 
        ## 用正则匹配,从 x_forwarded_for 中取得用户的原始IP  
## 例如 X-Forwarded-For: 202.123.123.11, 208.22.22.234, 192.168.2.100,...## 这里第一个 202.123.123.11 是用户的真实IP,后面其它都是经过的 CDN 服务器 
        ~^(?P<firstAddr>[0-9\.]+),?.*$  $firstAddr; 
    } 
        ## 通过 map 指令,我们为 nginx 创建了一个变量 $clientRealIp ,这个就是 原始用户的真实 IP 地址, 
## 不论用户是直接访问,还是通过一串 CDN 之后的访问,我们都能取得正确的原始IP地址 
 
    ## 每秒并发连接限制 
    limit_conn_zone $clientRealIp zone=TotalConnLimitZone:10m ;  
 
 
 
    ## 每秒请求数限制 
    limit_req_zone $clientRealIp zone=ConnLimitZone:10m  rate=10r/s; 
 
 
#未经过测试 以后有时间再做 

  

  

优化配置文件突破十万并发

#user  nobody; 
worker_processes  auto;  ##允许Nginx进程生成的worker process数 根据cpu内核数自动生成 
 
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000; 
#为每个进程分配cpu,上例中将8 个进程分配到8 个cpu,当然可以写多个,或者将一个进程分配到多个cpu 
 
#error_log  logs/error.log 
#error_log  logs/error.log  notice; 
#error_log  logs/error.log  info; 
#pid        logs/nginx.pid; 
 
worker_rlimit_nofile 102400;	 
#这个指令是指当一个nginx 进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx 进程数相除, 
#但是nginx 分配请求并不是那么均匀,所以最好与ulimit -n 的值保持一致 
 
events { 
    use	epoll;			##配置事件驱动模型,使用epoll 的I/O 模型 
    worker_connections  102400;	##配置最大连接数,每个进程允许的最多连接数,理论上每台nginx 服务器的最大连接数 
				#为worker_processes*worker_connections 
} 
 
 
 
http { 
    include       mime.types;	##定义MIME-Type 
    default_type  application/octet-stream; 
    log_format  main  '$remote_addr - [$time_local] - "$request"'  ##定义日志类型,远程地址、时间、请求内容、请求状态、cookie值 
	              ' - $status' $http_cookie; 
 
    types_hash_max_size 2048;	##多servername需设置 
    client_header_buffer_size 4k;   ##客户端请求头部的缓冲区大小,这个可以根据你的系统分页大小来设置,一般一个请求 
#头的大小不会超过1k,不过由于一般系统分页都要大于1k,所以这里设置为分页大小。分页大小可以用命令getconf PAGESIZE 取得 
    open_file_cache max=102400 inactive=20s; 
#这个将为打开文件指定缓存,默认是没有启用的,max 指定缓存数量,建议和打开文件数一致,inactive 是指经过多长时间文件没被请求后删除缓存 
    open_file_cache_valid 30s; 
#这个是指多长时间检查一次缓存的有效信息 
    server_names_hash_bucket_size 512;	       
    sendfile        on; 
    server_tokens off;		##关闭默认版本号 
    keepalive_timeout  20;	##定义链接超时时间,防止慢速dos攻击 
    client_header_timeout 15;   ##请求头超时时间 
    client_body_timeout	15;	##请求体超时时间 
    reset_timedout_connection on;	##关闭不响应的客户端连接,释放客户端占有内存 
    send_timeout 10;		##客户端两次读取时间,如果这段时间里没有读取任何数据,nginx就会关闭连接 
	 
########################内核优化##################### 
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 秒,3你可以按这个设置,但要记住的是,即使你的机器是一个轻载的WEB 服务器, 
#也有因为大量的死套接字而内存溢出的风险,FIN- WAIT-2 的危险性比FIN-WAIT-1 要小,因为它最多只能吃掉1.5K 内存,但是它们的生存期长些。 
net.ipv4.tcp_keepalive_time = 30 
当keepalive 起用的时候,TCP 发送keepalive 消息的频度。缺省是2 小时。 
 
 
	 
    server {		##直接访问IP 反馈500错误 
	listen 80 default; 
	return 500; 
	error_page   500 502 503 504  /50x.html; 
        location = /50x.html { 
            root   html; 
	  } 
	} 
 
     server { 
        listen       80; 
        server_name  www.test.com www.test.com.cn; 
        #rewrite域名跳转 
        if ($host != 'www.test.com' ) { 
        rewrite ^/(.*)$ http://www.test.com/$1 permanent;   #调转 
        } 
        access_log /data/web_log/test/access.log main; 
        error_log /data/web_log/test/error.log debug; 
        
       #设置cookie值 
        if ($http_cookie ~* "(.*)$"){ 
        #if ($http_cookie ~* "JCRB_VOTE=([A-Z0-9]*)"){ 
                set $guid $1; 
        } 
     
    #当访问.txt结尾的文件时向/data/yanz目录里找 
        location ~ \.txt$ { 
        root /data/yanz; 
        } 
 
        location / { 
            root   /data/test; 
            index  index.html index.xml; 
            ssi on; 
            ssi_silent_errors on; 
            ssi_types text/shtml; 
        } 
 
        error_page  404              /404.html; 
        location = /404.html { 
	 root html; 
	} 
 
        error_page   500 502 503 504  /50x.html; 
        location = /50x.html { 
            root   html; 
        } 
 
    } 
 
}   

  

  


标签:linuxnginx
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
KIKK导航

KIKK导航

关注我们