linux 链接 time wait,Linux很多TIME_WAIT问题的解决方法

linux 链接 time wait,Linux很多TIME_WAIT问题的解决方法_第1张图片

0x00 问题

一个服务器运行用nginx的web服务,由于php需要频繁的访问数据库,而且使用的都是短链接,因此一段时间内产生并保持大量的TIME_WAIT。

$ netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

LAST_ACK 11

LISTEN 11

SYN_RECV 42

ESTABLISHED 172

FIN_WAIT1 13

FIN_WAIT2 11

CLOSING 1

SYN_SENT 38

TIME_WAIT 3792

0x01 危害

Linux很多TIME_WAIT,相应的会占用大量的系统资源,同时还会占用大量的端口和连接数,导致新来的请求无法被服务器及时响应处理;默认情况下,linux临时端口号范围是(32768,61000),本机可用于调用的端口约3万个,进而导致调用后端服务阻塞,页面响应变慢;

0x02 解决办法

根据以上分析,需要对系统内核参数进行优化,启用TIME_WAIT连接重用,TIME_WAIT连接回收、缩短连接保持时间、增加可用端口数:

vi /etc/sysctl.conf

net.ipv4.tcp_tw_reuse = 1 # 连接重用

net.ipv4.tcp_tw_recycle = 1 # 连接回收

net.ipv4.tcp_fin_timeout = 30 # 连接保持时间

net.ipv4.ip_local_port_range=1024 65000 # 增加端口数量

sysctl -p # 生效

通常情况下,设置前三条即可,减少了TIME_WAIT数量,端口占用也会缓解,但如果服务的并发量本来就很大,增加端口数量就很有必要;

优化生效后,TIME_WAIT明显减少:

$ netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

LAST_ACK 17

LISTEN 11

SYN_RECV 25

ESTABLISHED 107

FIN_WAIT1 9

FIN_WAIT2 23

CLOSING 2

SYN_SENT 14

TIME_WAIT 217

0x03 注意

需要确认 net.ipv4.tcp_timestamps = 1 是打开的(默认是打开的),因为只有timestamps打开了,net.ipv4.tcp_tw_recycle = 1 才会生效;

当 net.ipv4.tcp_timestamps 和 net.ipv4.tcp_tw_recycle 同时打开,对通过NAT网关访问服务器的连接会有问题,因为 tcp_tw_recycle/tcp_timestamps 都开启的条件下,60s内同一源ip主机的socket connect请求中的timestamp必须是递增的。而timestamp时间为系统启动到当前的时间,因此,不同客户端的timestamp不相同,timestamp 大的客户端可以正常访问,timestamp小的客户端则访问失败; 这种情况建议关闭 tcp_tw_recycle 选项,而不是 timestamp;因为 在tcp timestamp关闭的条件下,开启tcp_tw_recycle是不起作用的;而tcp timestamp可以独立开启并起作用。

0x04 总结

使用过多的短连接,导致了大量的TIME_WAIT;在服务设计时,应尽量采用长连接,连接池,KEEPALIVE等技术减少对后端的连接次数,提高连接的效率。

这次的问题,因PHP本身的特性,一个页面处理完成后,所有相关连接就断了,不太好使用长连接,连接池,KEEPALIVE技术;

0x05 确有频繁访问时,以上设置并不能减少 TIME_WAIT

当服务器却又很多用户频繁访问,以上针对系统的优化并不能达到效果。

原因试试php页面访问mysql采用的是短连接,用完就断开,频繁访问必然产生很多TIME_WAIT;

解决办法, 采用长连接的方式访问数据库,这里采用php的PDO扩展访问mysql,长连接还能提高性能:

$db_host="localhost:3306";

$db_username="user";

$db_password="";

$dsn = "mysql:host=$db_host;dbname=$database;";

try {

$sql = new PDO($dsn,$db_username,$db_password,array(PDO::ATTR_PERSISTENT => true));//PDO::ATTR_PERSISTENT => true 表示采用持久化连接;

} catch(PDOException $e){

die($e->getMessage());

}

try{

$result = $sql->query("select * from accounts where id = '".$sn."';");

} catch(PDOException $e){

die($e->getMessage());

}

$rows = $result->fetchAll(PDO::FETCH_ASSOC);// 这里要注意,查询结果只能调用一次fetch,后面再调用就返回空了,所以这个地方第一次调用就要保存下来给后续使用;

if(count($rows) == 1){

$row = $rows[0];// 多个结果时,foreach 遍历即可

.....

}

或者:

foreach($dbh->query('SELECT * from FOO') as $row) {

print_r($row);

}

php7.2 设置支持PDO扩展

vim /etc/php/7.2/cli/php.ini 这个配置文件是配置php命令行运行环境;

vim /etc/php/7.2/fpm/php.ini 这个配置 php-fpm,里面 extension=pdo_mysql 是注释掉的,但是 /etc/php/7.2/fpm/conf.d/20-pdo_mysql.ini 中有定义,所以应该是默认支持pdo的;

​ 打开注释: extension=pdo_mysql 即可;

系统优化后,再使用PDO长连接访问mysql,TIME_WAIT连接数,直线下跌几乎没有;

版权所有丨如未注明 , 均为原创丨

转载请注明原文链接:Linux很多TIME_WAIT问题的解决方法

你可能感兴趣的:(linux,链接,time,wait)