1. tw_reuse,tw_recycle 必须在客户端和服务端timestamps 开启时才管用(默认打开)mysql
2. tw_reuse 只对客户端起做用,开启后客户端在1s内回收linux
3. tw_recycle 对客户端和服务器同时起做用,开启后在 3.5*RTO 内回收,RTO 200ms~ 120s 具体时间视网络情况。内网情况比tw_reuse 稍快,公网尤为移动网络大多要比tw_reuse 慢,优势就是可以回收服务端的TIME_WAIT数量nginx
* linux 内核文档中,对net.ipv4.tcp_tw_recycle的描述并非很明确。
web
tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)[译者注:来自linux man tcp的描述]redis
Enable fast recycling of TIME-WAIT sockets. Enabling this option is not recommended since this causes算法
problems when working with NAT (Network Address Translation).sql
启用TIME-WAIT状态sockets的快速回收,这个选项不推荐启用。在NAT(Network Address Translation)网络下,会致使大量的TCP链接创建错误。后端
* 假如启用net.ipv4.tcp_tw_recycle,能够用于快速减小在TIME-WAIT状态TCP链接数。可是,在TCP(7)手册中,参数net.ipv4.tcp_tw_recycle 很是蛋疼,尤为是在普通用户家中,有多台设备,或者网吧、公司等多台设备,共用同一个NAT设备环境下,TW回收选项是颇有问题的面向公共服务器做为它不会把手链接两台不一样的计算机上,这问题很难发现,无从下手安全
* Connection table slot链接表槽服务器
处于TIME-WAIT状态的TCP链接,在连接表槽中存活1分钟,意味着另外一个相同四元组(源地址,源端口,目标地址,目标端口)的链接不能出现,也就是说新的TCP(相同四元组)链接没法创建。对于web服务器来讲,目标地址、目标端口都是固定值。若是web服务器是在L7层的负载均衡后面,那么源地址更是固定值。在LINUX上,做为客户端时,客户端端口默承认分配的数量是3W个(能够在参数net.ipv4.up_local_port_range上调整)。
这意味着,在web服务器跟负载均衡服务器之间,每分钟只有3W个端口是处于established状态,也就大约500链接每秒。若是TIME-WAIT状态的socket出如今客户端,那这个问题很容易被发现。调用connect()函数会返回EADDRNOTAVAIL,程序也会记录相关的错误到日志。若是TIME-WATI状态的socket出如今服务端,问题会很是复杂,由于这里并无日志记录,也没有计数器参考。不过,能够列出服务器上当前全部四元组链接的数量来确认
* 解决办法:增长四元组的范围,这有不少方法去实现。(如下建议的顺序,实施难度从小到大排列)
修改net.ipv4.ip_local_port_range参数,增长客户端端口可用范围。
增长服务端端口,多监听一些端口,好比8一、8二、83这些,web服务器前有负载均衡,对用户友好。
增长客户端IP,尤为是做为负载均衡服务器时,使用更多IP去跟后端的web服务器通信。
增长服务端IP。
固然了,最后的办法是调整net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle。但不到万不得已,千万别这么作。
net.ipv4.tcp_tw_recycle
这种机制也依赖时间戳选项,这也会影响到全部链接进来和链接出去的链接。「译者注:linux上tcp_timestamps默认开启」
TIME-WAIT状态计划更早的过时:它将会在超时重发(RTO)间隔后移除(底层会根据当前链接的延迟情况根据RTT来计算RTO值,上篇《PHP-FPM中backlog参数变动的一些思考》也有提到过,比较复杂的算法)。能够执行ss指令,获取当前存活的TCP链接状态,查看这些数据。「译者注:linux指令ss的结果中rto,rtt值单位均为ms」
与其功能类似的参数net.ipv4.tcp_tw_reuse,手册里稍微有点描述,以下:
tcp_tw_reuse (Boolean; default: disabled; since Linux 2.4.19/2.6)
Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. It
should not be changed without advice/request of technical experts.
//从协议设计上来看,对于TIME-WAIT状态的sockets重用到新的TCP链接上来讲,是安全的。(用于客户端时的配置)
net.ipv4.tcp_tw_reuse
TIME-WAIT状态是为了防止不相关的延迟请求包被接受。但在某些特定条件下,颇有可能出现,新创建的TCP链接请求包,被老链接(一样的四元组,暂时仍是TIME-WAIT状态,回收中)的链接误处理。RFC 1323 实现了TCP拓展规范,以保证网络繁忙状态下的高可用。除此以外,另外,它定义了一个新的TCP选项–两个四字节的timestamp fields时间戳字段,第一个是TCP发送方的当前时钟时间戳,而第二个是从远程主机接收到的最新时间戳。
启用net.ipv4.tcp_tw_reuse后,若是新的时间戳,比之前存储的时间戳更大,那么linux将会从TIME-WAIT状态的存活链接中,选取一个,从新分配给新的链接出去的TCP链接。
连出的TIME-WAIT状态链接,仅仅1秒后就能够被重用了。
最后注意的是:
* 最合适的解决方案是增长更多的四元组数目,好比,服务器可用端口,或服务器IP,让服务器能容纳足够多的TIME-WAIT状态链接。在咱们常见的互联网架构中(NGINX反代跟NGINX,NGINX跟FPM,FPM跟redis、mysql、memcache等),减小TIME-WAIT状态的TCP链接,最有效的是使用长链接,不要用短链接,尤为是负载均衡跟web服务器之间。尤为是链家事件中的PHP连不上redis。
* 在服务端,不要启用net.ipv4.tcp_tw_recycle,除非你能确保你的服务器网络环境不是NAT。在服务端上启用net.ipv4.tw_reuse对于链接进来的TCP链接来讲,并无任何卵用。
* 在客户端(尤为是服务器上,某服务以客户端形式运行时,好比上面提到的nginx反代,链接着redis、mysql的FPM等等)上启用net.ipv4.tcp_tw_reuse,还算稍微安全的解决TIME-WAIT的方案。再开启net.ipv4.tcp_tw_recycle的话,对客户端(或以客户端形式)的回收,也没有什么卵用,反而会发生不少诡异的事情(尤为是FPM这种服务器上,相对nginx是服务端,相对redis是客户端)。