Linux高并发性能优化方案88bifa必发唯一官网

减少TIME,GNU Linux高并发性能优化方案

代码运行一段时间后,会报下面的错误。

/***********************************************************
* Author : Samson
* Date : 07/14/2015
* Test platform:
* gcc (Ubuntu
4.8.2-19ubuntu1) 4.8.2
* GNU bash, 4.3.11(1)-release (x86_64-pc-linux-gnu)
* Nginx version:
* Nginx 1.6.2
* Nginx 1.8.0
*
*********************************************************/

[Predis\Connection\ConnectionException]                              

GNU Linux高并发性能优化方案

Error while reading line from the server. [tcp://127.0.0.1:6379]

在GNU
Linux系统中,影响连接个数的因素主是由于单个进程能够打开的最大文件数、端口数量决定的;而一个基于tcp的服务器的并发,除了上文说过的两个因素外,还有因为主要的tcp连接的很多属性,而问题最大的则是连接断开后的连接会在TIME_WAIT状态一直存在60秒,这就造成了在大量高并发的情况下当连接为此TIME_WAIT状态时没有可用连接。

 

1、修改端口号范围:
默认范围:

最初的怀疑是连接数过多,导致连接不上服务器,出现上述错误。查看进程,发现大量redis状态为TIME_WAIT的tcp连接。

cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000

 

众所周知,端口号的范围为0~65535,知名端口号介于1~255之间,256~1023之间的端口号通常都是由系统占用,所以我们需要更多可以使用的端口号的话,那么就需要修改系统中对端口使用范围变量;

首先考虑的是,减少TIME_WAIT的进程,保持随时可以连接到服务器。所以想到的减少TIME_WAIT状态的进程,将进程快速回收。修改内核参数sysctl.conf,net.ipv4.tcp_timestamps=1(1为开启),开启快速回收net.ipv4.tcp_tw_recycle=1。tw_recycle是通过时间戳判断哪个是最新的进程,将不是最新的TIME_WAIT的进程回收,所以需要先开启tcp_timestamps。

修改方法:
1)、echo “1024 65535” > /proc/sys/net/ipv4/ip_local_port_range
2)、在/etc/sysctl.conf中进行如下的设置:
net.ipv4.ip_local_port_range=1024 65535
然后执行: sysctl -p 对这些设置进行生效;

修改后观察,果然没有继续报错。

3)、直接使用命令进行系统变量的优化
sysctl -w net.ipv4.ip_local_port_range=1024 65535

 

若端口不够用的时候报错信息
若没有空闲的端口可以使用时,将会报错,如:
connect() to ip:80 failed (99: Cannot assign requested address)

但是使能快速回收TIME_WAIT进程,可能会丢包,导致没有收到应答,不能成功建立连接。但这种办法也不是最佳解决办法,尤其修改内核参数,涉及环节太多,需深入了解才可修改。

注意:
修改了端口的范围后,若是有多个服务在一台设备上时,若是先被其它服务先行启动将另外的服务的“众所周知”的端口给占用了,那么这个问题就比较不太好处理,在这种情况下,对于需要监听的服务进行先行启动,将服务要使用的“众所周知”的端口先行占用,就不会发生比较麻烦的情况了。

 

2、修改系统所有进程能够打开的文件数:
cat /proc/sys/fs/file-max
203466

底层不去修改,就从predis客户端入手,源码发现有read_write_timeout这个参数,可以设置超时时间,这样读取流数据时就不会报错。Predis作者建议设置关闭redis.conf中timeout(修改timeout
0),表示不关闭与客户端的连接,我感觉这样比较耗费资源,可以适当增加timeout时间。

若要修改的话:
echo 403466 > /proc/sys/fs/file-max

 

3、对于处理TIME_WAIT的问题,通过设置以下两项可以极大地提高并发
在进行了通信完毕后,完成通信的连接差不多在秒级间就进行了回收,经测试,再使用netstat
-ntp,都不会再看到刚才使用的连接,但是在官方文档中说明了(Default value
is 0. It should not be changed without advice/request of technical
experts.)使用这两种方式需要非常谨慎;如下(修改方法请参看上面的修改方式):
net.ipv4.tcp_tw_reuse = 1

所以这次暂时是这样解决的,设置read_write_timeout=-1和redis.conf的timeout参数。

//表示开启重用。允许将TIME-WAIT
sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1
//表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

其实有很多解决方式,后续可以继续寻找一个更优的方案。

由于以上两项在官方文档中有这样的描述”It should not be changed without
advice/request of technical
experts.“,也即是说,这两项在某些情况下会产生负作用或影响;

 

可能出现的影响:
net.ipv4.tcp_tw_recycle是与net.ipv4.tcp_timestamps是密切相关的,而net.ipv4.tcp_timestamps默认是开启的,当tcp_tw_recycle和tcp_timestamps同时打开时会激活TCP的一种隐藏属性:缓存连接的时间戳。60秒内,同一源IP的后续请求的时间戳小于缓存中的时间戳,内核就会丢弃该请求。

比较了常见的两个php连接redis客户端,phpredis和predis。Laravel中使用的predis,其中连接redis使用connect,当请求结束连接关闭。而phpredis使用pconnect连接,依赖于php-fpm,php-fpm不关闭,连接一直都在。再次使用pconnect,连接会被重用,不会再次新建。

什么样的场景会使时间戳会小于缓存中的时间戳呢?
类似的故障场景:
多个客户端通过一个NAT访问一台服务器,因为NAT只改IP地址信息,但不会改变timestamp(TCP的时间戳不是系统时间,而是系统启动的时间uptime,所以两台机器的的TCP时间戳一致的可能性很小),那么就会出现请求被丢弃的情况,所以很容易造成连接失败的情况。
服务器上开启了tcp_tw_recycle用于TIME_WAIT的快速回收故障现象和分析步骤:
1) 通过NAT出口的多个客户端经常请求Web服务器无响应;
2)
在服务器抓包,发现服务端可以收到客户端的SYN请求,但是没有回应SYN,ACK,也就是说内核直接将包丢弃了。

 

解决方法:
1)
关闭服务其端的tcp_timestamps,故障可以解决,但是这么做存在安全和性能隐患,强烈建议不关闭此变量;
2)
关闭tcp_tw_recycle,故障也可以解决。推荐NAT环境下的机器不要开启该选项;
3)调整网络拓扑避免NAT的这种类似的情况;
4)客户端使用同一个NTP服务进行时间同步,使时间同步避免timestamp差异;

后来查看predis源码发现,persistent这个参数,手册说明都没有提到,但是看字面意思,可能类似于pconnect方式的持久连接,这个后续再研究一下。

其它优化参数
net.ipv4.tcp_fin_timeout = 30
//表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。
net.ipv4.tcp_keepalive_time = 1200
//表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.tcp_max_tw_buckets = 5000
//表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,
//TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为5000。
//对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,

//此值和/proc/sys/net/ipv4/tcp_max_syn_backlog是一样的,也是对listen()函数中的backlog参数的限制,按照文档中的说明,应该最好是设置成和/proc/sys/net/ipv4/tcp_max_syn_backlog一样的值,此值的默认值是128:
cat /proc/sys/net/core/somaxconn
128
net.core.somaxconn = 40000

//指定未完成连接队列的最大长度,默认为1024个,是对socket的listen()函数中的backlog数量的限制,若服务器过载可以增大此值;
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
1024
net.ipv4.tcp_max_syn_backlog = 40000

4、调整每个进程最大打开文件描述符限制
调整文件描述符限制:

$ ulimit -n
1024
修改此值,ulimit -n 4096

$vi /etc/security/limits.conf
//Setting Shell Limits for File Descriptors
*soft nofile 8192
*hard nofile 8192

这二者的区别,在/etc/security/limits.conf配置文件中配置好后,进行重启后再次使用ulimit
-n将得到的值是8192.

5、通过重新编译内核代码减少TCP连接的TIME-WAIT时间
在内核代码中的include/net/tcp.h文件中,TIME-WAIT的定义如下:
//#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy
TIME-WAIT
* state, about 60 seconds */
可以通过修改TCP_TIMEWAIT_LEN的值,来加快连接的释放;修改后,内核进行编译后进行替换。

Nginx的配置与系统环境变量间的关系
系统默认的是1024,若在Nginx的配置文件中,配置了worker_connections
4096;后,再启动时,会出现这样的一个警告:
nginx: [warn] 4096 worker_connections exceed open file resource
limit: 1024
Nginx中的这些和系统变量有关的,是根据系统中的配置而进行设置的,若大于了系统变量的范围的话,不会生效,会被默认成系统的值,如每个worker进行能够打开的文件数量就被默认成系统的值1024;

注意:
修改内核变量是有风险的,最好是在测试环境进行测试通过,再将配置平移到生产环境。

REF:
IPV4和IPV6的内核参数各项的意义及取值:

关于/proc目录下的主要项的介绍:

本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-07/119962.htm

88bifa必发唯一官网 1