现象:
服务器产生大量SYN_RECV连接,而系统默认SYN_RECV为1024,如果系统当前的syn_recv达到系统默认值,Nginx会不处理新来的连接,导致影响业务。
分析:
目前是使用f5的layer4模式转发请求,故所有tcp连接的处理都在服务器端完成,有可能是网络质量不好而导致产生大量的syn_recv,使用netstat命令查看TOP 10 syn_recv ip,发现都是移动网关过来的,初步判断可能是移动网络慢对方无法接受服务器返回的包而产生syn_recv,尤其是如下ip比较明显:211.139.92.11(甘肃省兰州市移动)。
解决方法:
1.F5改为standard方式,所有tcp连接由F5处理,后端只关注业务,故障解决,但
加大F5的性能负荷。
2.服务器修改内核,使其能容纳更多的syn_recv,加大网络连接;
修改内核参数如下:
1)net.ipv4.tcp_max_syn_backlog = 65536
该参数是SYN队列的长度,默认为1024,修改为65536,加大SYN队列长度可以容纳更多等待连接的网络连接数
2)net.core.netdev_max_backlog = 32768
该参数决定了,网络设备接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目,默认值为300。根据需要调整改值,不要设置的过大。
3)net.core.somaxconn = 32768
该参数用来限制监听队列最大数据包的数量,超过这个数量就会导致链接超时或者触发重传机制。也就是说,web应用中listen函数的backlog会给我们内核参数的net.core.somaxconn 限制到128,在高突发的请求中可能会导致链接超时或者触发重传;例如:nginx的默认的backlog队列为511,如果内核参数net.core.somaxconn的值低于511,那么nginx的backlog队列将被限制为该参数的值。此外,可以在nginx的配置中增加监听队列的数量,当然前提是不能超过net.core.somaxconn的值。
server {
listen 80 deafult backlog=8192;
server_name http://www.libertyvps.com]www.libertyvps.com;
}
4)net.ipv4.tcp_syncookies = 1
该参数是一个开关,是否打开SYN Cookie功能,该功能可以防止部分SYN攻击,默认为0关闭,1开启。
注意:该选项千万不能用于那些没有收到攻击的高负载服务器,如果在日志中出现synflood消息,但是调查发现没有收到synflood攻击,而是合法用户的连接负载过高的原因,你应该调整其它参数来提高服务器性能。参考:
5)net.ipv4.tcp_synack_retries = 0
默认值是5,对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。(可以根据下面的tcp_syn_retries 来决定这个值)
6)net.ipv4.tcp_syn_retries = 0
默认值是5,对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的连接,是由tcp_retries1 决定的)
注:5)6)这2项可根据业务的特性灵活调整,我这里设置为0,对业务本身没有很大的影响,查看连接及日志没有减少的变化。
7)由于我们的业务是移动互联网业务,根据其特殊性,如修改了如下2个参数,会对业务有一定影响,请不要调整,保持默认即可。
net.ipv4.tcp_rw_reuse = 0
net.ipv4.tcp_rw_recycle = 0
通过查看服务器,有大量的TIME_WAIT连接,而很多是出现在nginx+php-cgi的ip转发连接方式,建议可调整为通过unix socket方式访问,减少不必要的网络消耗。
经过以上调整,服务器在容纳更多的SYN_RECV亦保证nginx能正常提供web访问,查看系统负载,保持在1以下。
首先我们需要弄清楚SYN_RCVD状态是怎样产生的,通过TCP状态转换图(如下图)我们可以清楚的看到,SYN_RCVD是TCP三次握手的中间状态,是服务端口(监听端口,如应用服务器的80端口)收到SYN包并发送[SYN,ACK]包后所处的状态。这时如果再收到ACK的包,就完成了三次握手,建立起TCP连接。
如果服务器上出现大量的SYN_RCVD状态的TCP连接说明这些连接一直没有收到ACK包,这主要有两种可能,一种是对方(请求方或客户端)没有收到服务器发送的[SYN,ACK]包,另一种可能是对方收到了[SYN,ACK]包却没有ACK。
对于第一种情况一般是由于网络结构或安全规则限制导致(SYN,ACK)包无法发送到对方,这种情况比较容易判断:只要在服务器上能够ping通互联网的任意主机,基本可以排除这种情况。
对于第二种情况要稍微复杂一些,这种情况还有两种可能:一种是对方根本就不打算ACK,一般在对方程序有意为之才会出现,如SYN Flood类型的DOS/DDOS攻击;另一种可能是对方收到的[SYN,ACK]包不合法,常见的是SYN包的目的地址(服务地址)和应答[SYN,ACK]包的源地址不同。这种情况在只配置了DNAT而不进行SNAT的服务网络环境下容易出现,主要是由于inbound(SYN包)和outbound([SYN,ACK]包)的包穿越了不同的网关/防火墙/负载均衡器,从而导致[SYN,ACK]路由到互联网的源地址(一般是防火墙的出口地址)与SYN包的目的地址(服务的虚拟IP)不同,这时客户机无法将SYN包和[SYN,ACK]包关联在一起,从而会认为已发出的SYN包还没有被应答,于是继续等待应答包。这样服务器端的连接一直保持在SYN_RCVD状态(半开连接)直到超时。
资料:
http://baike.baidu.com/view/1520054.htm
http://bbs.linuxtone.org/thread-1686-1-1.html
http://rhomobi.com/topics/47