先贴一个生产环境下5000连接Nginx的运行服务器的连接状态:
01 |
[root@weba ~] # netstat -na |awk '{print $6}'| sort |uniq -c |sort -nr |
02 |
4963 ESTABLISHED port80 |
03 |
706 FIN_WAIT1 port80 |
04 |
134 TIME_WAIT port80 |
05 |
74 CONNECTED |
06 |
41 |
07 |
33 LISTEN |
08 |
25 LAST_ACK |
09 |
18 SYN_RECV |
10 |
12 STREAM |
11 |
1 I-Node |
12 |
1 Foreign |
13 |
1 FIN_WAIT2 |
放一张图,要搞清楚怎么调整参数,这张图相当重要。看不太明白不要紧,后面会详细讲到这张图
三次握手以及其中的各种状态:
SYN(Synchronize Sequence Numbers)。
同步序列编号
ACK (ACKnowledge Character)
在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。
=================================
client发送syn至server
此时客户端的状态变为SYN_SENT
client(syn=j)====>server
server收到syn,并发送syn+ack到client,
这种过程server状态由listen变为SYN_RECV,并等待客户端再次发来ack数据
client<=========server(syn=k,ack=j+1)
client接收到server发过来的syn+ack,并向服务端发送ACK,服务器接收后由SYN_RECV变为ESTABLISHED
client(ACK(ack=k+1))========>server
此种情况下,服务端在三次握手的变迁是
LISTEN->SYN_RECV ->ESTABLISHED
客户端的三次握手的变迁是
SYN_SENT ->ESTABLISHED
====================================
在这种过程中要注意的
一、首先server有个用来接收client发送的syn并对syn进行排队的队列,如果队列满了,新的请求不被接受。
此队列长度控制参数:
net.ipv4.tcp_max_syn_backlog
对应文件(/proc/sys/net/ipv4/tcp_max_syn_backlog ) 默认是1024
1 |
[root@web] # cat /proc/sys/net/ipv4/tcp_max_syn_backlog |
2 |
1024 |
二、然后是SYN-ACK重传:当server向client发送syn+ack没有收到相应,server将重传,然后再重传。。。控制这个重传次数的参数是
tcp_synack_retries
对应文件(/proc/sys/net/ipv4/tcp_synack_retries )默认值是5,对应于180秒左右时间
1 |
[root@web ~] # cat /proc/sys/net/ipv4/tcp_synack_retries |
2 |
5 |
关于tcp_synack_retries的英文解释: |
The maximum number of times a SYN/ACK segment for a passive TCP connection will be retransmitted. This number should not be higher than 255 . The default value is 5 . |
备注:与此相对应的client的参数是: |
tcp_syn_retries |
The maximum number of times initial SYNs for an active TCP connection attempt will be retransmitted. This value should not be higher than 255 . The default value is 5 , which corresponds to approximately 180 seconds. |
三、关于tcp_syncookies
SYN Cookie原理及其在Linux内核中的实现
http://www.ibm.com/developerworks/cn/linux/l-syncookie/?ca=dwcn-newsletter-linux
SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。
1 |
[root @web ~]# cat /proc/sys/net/ipv4/tcp_syncookies |
2 |
1 |
===========================================
典型故障处理
如果服务器syn_recv的条数过多,可以采取的操作是:
减少server==>client重传syn+ack的次数。
加大syn队列长度,防止无法响应新的连接
1 |
echo "net.ipv4.tcp_max_syn_backlog = 4096" >>/etc/sysctl.conf |
2 |
echo "net.ipv4.tcp_synack_retries = 1" >>/etc/sysctl.conf |
3 |
sysctl -p |
当受到syn攻击的时候,启用syn-cookie(默认启用,在/etc/sysctl.conf里本身就有参数配置)
1 |
echo 1 >/proc/sys/net/ipv4/tcp_syncookies |
=================================================
四次握手
下面说tcp/ip的第四次握手,分析主动关闭和被动关闭两种。
A:因为如果是CLIENT端主动断掉当前连接,那么双方关闭这个TCP连接共需要四个packet:
setup
Client ---> FIN(M) ---> Server
client发送一个FIN给server,(说它不跟你玩了),client由ESTABLISHED->FIN_WAIT1
Client <--- ACK(M+1) <--- Server
SER VER收到fin后发送ack确认(拿出两人信物),状态由ESTABLISHED->close_wait
client收到server的ack确认,只是改变状态ESTABLISHED->FIN_WAIT1->FIN_WAIT2,继续等server发送数据。
Client <-- FIN(N) <-- Server
server继续发送FIN到client(好就不玩了吧),状态ESTABLISHED->close_wait->LAST_ACK,等待client发送ack做最后的确认
Client --> ACK(N+1) --> Server
client收到FIN,马上发送ack确认,状态ESTABLISHED->FIN_WAIT1->FIN_WAIT2->TIME_WAIT[2MSL超时]->closed
server收到ack确认,状态ESTABLISHED->close_wait->LAST_ACK->CLOSED.
client关闭连接很好想,有点要搞清楚的是,server端什么时候会发起丢掉连接的操作:
有些是应用程序控制的。nginx.conf为例
keepalive_timeout 0;
[root@lvs-2 ~]# curl -I http://www.XXX.com
Connection: close
keepalive_timeout 600;
[root@lvs-2 ~]# curl -I http://www.XXX.com
Connection: keep-alive
这种规定了连接时间的,到了时间连接会断掉。
如果没有规定的,则按照系统keepalived定时器的设置进行,具体参数如下:
[root@web]# sysctl -a|grep tcp_keepalive
1 |
net.ipv4.tcp_keepalive_intvl = 75 |
2 |
net.ipv4.tcp_keepalive_probes = 9 |
3 |
net.ipv4.tcp_keepalive_time = 7200 |
4 |
连接两端一直没发送数据,间隔120分钟,两小时后开始第一次探测,间隔75秒后第二次探测,探测9次,最后放弃连接。有四种探测的情况,详见 |
5 |
http://www.360doc.com/content/09/1231/16/96202_12383765.shtml |
四种状况其实最后一种没什么意义,能考虑到下面三种就行了
1 client正常,每进行一次通讯,net.ipv4.tcp_keepalive_time重置一次。
2 一直到7200+75*9后也无法获取client反馈信息,则认为client已经关闭并终止连接(连接超时)
3 client重启, 收到探测后返回一个复位(RST)信息。server收到后终止连接(连接被对方复位)
====================================================================================
典型故障处理
一、网站服务器访问速度变慢,查看网站服务器连接,看到连接至数据库的连接中出现大量time_wait,多达400个。分析是网站服务器定时任务做大量读取数据库操作的时候产生的。
此情况出现在client.
根据上面讲的,time_wait对应2MSL超时,什么是2MSL?,是在client在四次握手的时候最后发送了ack确认给服务器后必然经过的一个时间。TIME_WAIT状态的目的是为了防止最后client发出的ack丢失,让server处于LAST_ACK超时重发FIN。配置2MSL时间长短的服务器参数,但这里不是优化的重要参数,我们需要的是Time_wait的连接可以重用,并且能迅速关闭。
2MSl的解释:
1 |
[root@bjweba ~] # sysctl -a | grep time | grep wait |
2 |
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 120 |
3 |
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 60 |
4 |
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait = 120 |
关于 ip_ct_tcp_timeout_time_wait
控制重用和迅速回收的参数是net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle。
这两个参数的具体英文解释和作用等我查到了再补充,网上讲的也不怎么清楚。
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0。
1 |
echo "net.ipv4.tcp_tw_reuse = 1" >>/etc/sysctl.conf |
2 |
echo "net.ipv4.tcp_tw_recycle = 1" >>/etc/sysctl.conf |
3 |
sysctl -p |
这样便可以看到time_wait的数量马上降下来了。
二、服务器端运行服务9090的c,或java程序用killall -9 服务名 kill掉后,出现
FIN_WAIT_2状态,新的服务无法启动。因为这个状态占据了服务端口。时间默认1分钟。
1 |
[root@web ~] # sysctl -a | grep time | grep fin |
2 |
net.ipv4.tcp_fin_timeout = 60 |
关于FIN_WAIT_2的解释http://httpd.apache.org/docs/1.3/misc/fin_wait_2.html
主要就是服务端主动发起关闭,此时服务端相当于一个client,在最后等对方发送最后一个FIN的却一直等不到,直至超时,控制这个超时的时间参数是tcp_fin_timeout
tcp_fin_timeout(0S) |
How many seconds to wait for a final FIN packet before the socket is forcibly closed. This is strictly a violation of the TCP specification, but required to prevent denial-of-service (DoS) attacks. The default value in 2.4 kernels is 60, down from 180 in 2.2. |
备注:TCP_LINGER2(tcp socket编程选项) |
The lifetime of orphaned FIN_WAIT2 state sockets. This option can be used to override the system wide sysctl tcp_fin_timeout on this socket. This is not to be confused with the socket (7) level option SO_LINGER. This option should not be used in code intended to be portable. |
1 |
echo "net.ipv4.tcp_fin_timeout = 30" >>/etc/sysctl.conf |
2 |
sysctl -p |
3 |
#echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout |
修改方式都有两种,一种是更改/etc/sysctl.conf,还有一种是直接写文件参数文件
http://steven-linux.javaeye.com/blog/540405
此外,还有控制tcp发送缓冲区,接收缓冲区大小的设置,能够使用端口范围的设置。
1 |
[root@web~] # cat /proc/sys/net/ipv4/ip_local_port_range |
2 |
32768 61000 |
这个是本地连接外地端口时开的动态端口,个人觉得默认就够了。如果有很频繁的要连接外面端口,可以设大。
1 |
#echo "5000 65535" > /proc/sys/net/ipv4/ip_local_port_range |
2 |
echo "net.ipv4.ip_local_port_range = 5000 65000" >> /etc/sysctl.conf |
3 |
sysctl -p |
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.ip_local_port_range = 5000 65000
参考
http://s135.com
http://linux.chinaunix.net/bbs/thread-901851-1-1.html
http://lavafree.javaeye.com/blog/700885
http://www.360doc.com/content/10/0613/18/4392_32918827.shtml
http://blog.csdn.net/sky04/archive/2010/07/28/5771797.aspx
http://linux.chinaunix.net/techdoc/net/2008/10/12/1037653.shtml
http://hi.baidu.com/puma_2002/blog/item/c9f04fc5838f7ad6d100609a.html
http://www.sudu.cn/info/html/edu/20080323/275185.html
http://www.oschina.net/bbs/thread/9464
http://blog.csdn.net/hbzhangjian/archive/2008/08/27/2837835.aspx
http://blog.c1gstudio.com/archives/138
http://blog.csdn.net/hbzhangjian/archive/2008/08/27/2837835.aspx
http://blog.163.com/shooow@126/blog/static/461242322007111515720873/
http://syslwx.spaces.live.com/