我喜欢通俗易通的文章,写文档的风格往往反映了整个人的内心和生活态度,轻松有乐趣才有学习的动力。复杂的东西简化更能提现作者的总结能力,这篇对tcp/ip协议以及linux内核参数调整的文章不错,贴上来收藏一下。以后要勤动手把收藏夹里的好文档全转过来,当然会标明作者,转自www.22455.com
先贴一个生产环境下5000连接Nginx的运行服务器的连接状态:
[root@weba ~]# netstat -na |awk '{print $6}'| sort |uniq -c |sort -nr
4963 ESTABLISHED port80
706 FIN_WAIT1 port80
134 TIME_WAIT port80
74 CONNECTED
41
33 LISTEN
25 LAST_ACK
18 SYN_RECV
12 STREAM
1 I-Node
1 Foreign
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
[root@web]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
1024
二、然后是SYN-ACK重传:当server向client发送syn+ack没有收到相应,server将重传,然后再重传。。。控制这个重传次数的参数是
tcp_synack_retries
对应文件(/proc/sys/net/ipv4/tcp_synack_retries )默认值是5,对应于180秒左右时间
[root@web ~]# cat /proc/sys/net/ipv4/tcp_synack_retries
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 behigherthan255. Thedefaultvalue is5.
备注:与此相对应的client的参数是:
tcp_syn_retries
The maximum number of times initial SYNs for an active TCP connection attempt will be retransmitted. This value should not behigherthan255. Thedefaultvalue is5, which corresponds to approximately180seconds.三、关于tcp_syncookies
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连接。
[root@web~]# cat /proc/sys/net/ipv4/tcp_syncookies
1
典型故障处理
如果服务器syn_recv的条数过多,可以采取的操作是:
减少server==>client重传syn+ack的次数。
加大syn队列长度,防止无法响应新的连接
echo "net.ipv4.tcp_max_syn_backlog = 4096" >>/etc/sysctl.conf
echo "net.ipv4.tcp_synack_retries = 1" >>/etc/sysctl.conf
sysctl -p
当受到syn***的时候,启用syn-cookie(默认启用,在/etc/sysctl.conf里本身就有参数配置)echo1 >/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 close_wait
client收到server的ack确认,只是改变状态ESTABLISHED->FIN_WAIT1->FIN_WAIT2,继续等server发送数据。
Client
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
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 7200
连接两端一直没发送数据,间隔120分钟,两小时后开始第一次探测,间隔75秒后第二次探测,探测9次,最后放弃连接。有四种探测的情况,详见
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的解释:
[root@bjweba ~]# sysctl -a | grep time | grep wait
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 60
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。
echo "net.ipv4.tcp_tw_reuse = 1" >>/etc/sysctl.conf
echo "net.ipv4.tcp_tw_recycle = 1" >>/etc/sysctl.conf
sysctl -p
这样便可以看到time_wait的数量马上降下来了。
二、服务器端运行服务9090的c,或java程序用killall -9 服务名 kill掉后,出现
FIN_WAIT_2状态,新的服务无法启动。因为这个状态占据了服务端口。时间默认1分钟。
[root@web ~]# sysctl -a | grep time | grep fin
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 waitfora 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 valuein2.4 kernels is 60, down from 180in2.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 usedincode intended to be portable.echo "net.ipv4.tcp_fin_timeout = 30" >>/etc/sysctl.confsysctl -p#echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
修改方式都有两种,一种是更改/etc/sysctl.conf,还有一种是直接写文件参数文件
http://steven-linux.javaeye.com/blog/540405
此外,还有控制tcp发送缓冲区,接收缓冲区大小的设置,能够使用端口范围的设置。
[root@web~]# cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000
这个是本地连接外地端口时开的动态端口,个人觉得默认就够了。如果有很频繁的要连接外面端口,可以设大
#echo "5000 65535" > /proc/sys/net/ipv4/ip_local_port_range
echo "net.ipv4.ip_local_port_range = 5000 65000" >> /etc/sysctl.conf
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