美文网首页
由nginx TW状态过多引发。。2018-01-17

由nginx TW状态过多引发。。2018-01-17

作者: tyssy | 来源:发表于2018-01-17 20:36 被阅读0次

    原因

    回顾之前的印象笔记时,查看到之前工作时记录的问题,查看之前解决办法时决定重新总结一下

    背景

    1. 后端使用nodejs处理连接,主要是做验证
    2. 前端使用nginx做控制,做7层反向proxy
    3. 验证时间很短,所以从nginx通过proxy_pass到后端的realserver之间使用的是短连接

    现象

    某次大型活动(国足比赛),请求数大增,nginx服务器出现大量TIME_WAIT状态的连接,查看方式:

    netstat方法:
    shell> netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
    ss查看比netstat要快:
    ss -s查看TW的数量
    

    解决办法

    Nginx做前端Proxy时TIME_WAIT过多的问题
    要点

    • 修改系统预留端口,防止自己进程监听的大于1024的端口被占用
    • 解决TW过多:修改nginx upstream配置中的keepalive时长并且在server段中使用http1.1(1.0不支持keepalive)

    upstream http_backend {
    server 127.0.0.1:8080;

    keepalive 16;
    }

    server {
    ...

    location /http/ {
    proxy_pass http://http_backend;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    ...
    }
    }

    其他

    通过这次解决过程顺便记录涉及到的其他内容

    TW状态产生的过程
    状态转换图

    TCP三次握手和四次挥手就不再啰嗦了,从上图可以看出,主动发送FIN的一方会受到TIME-WAIT的“惩罚”(就像主动提出分手会受到道德谴责一段时间)由RFC规定为2MSL,根据不同的操作系统MSL(Maximum Segment Lifetime)设定的时间不一样,Linux一般是30s且该值无法通过内核参数修改,必须自己重新编译内核才能修改此参数。

    穿插一点MSL的知识:MSL指的是报文段的最大生存时间,如果报文段在网络活动了MSL时间,还没有被接收,那么会被丢弃。关于MSL的大小,RFC 793协议中给出的建议是两分钟,不过实际上不同的操作系统可能有不同的设置,以Linux为例,通常是半分钟,两倍的MSL就是一分钟,也就是60秒,并且这个数值是硬编码在内核中的,也就是说除非你重新编译内核,否则没法修改它:

    #define TCP_TIMEWAIT_LEN (60*HZ)
    
    TW产生的原因

    有的人看到这就可能会问:为什么需要TIME-WAIT这个状态?这个状态纯粹是坑爹啊。。。其实主要是由于早期TCP设计时是用于建立有保障的连接的,由于当时网络底层的设备远没有现在这么发达,所以四次挥手时为了防止出现最后一个ACK对方没有收到并再次重传FIN接着导致:

    • 连接已经关闭,再次收到FIN回复一个RST;
    • 新的连接已经建立,FIN包会干扰到新的连接;
      总之,不管怎么样都会导致TCP不再可靠因此TW状态是必须存在的,由产生原因很容易想到为什么是2MSL,因为最坏的情况下一来一回刚好两段MSL。
    如何控制TIME-WAIT数量

    网络上大部分文章谈到解决TW状态都会提到以下内核参数

    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1
    tcp_timestamps
    net.ipv4.tcp_fin_timeout

    实际上 net.ipv4.tcp_fin_timeout 控制的是fin_wait_2这个状态的超时时间,重用回收和时间戳的机制可以看下面的文章描述:
    这个链接说明很清楚了

    要点

    • ip_conntrack:顾名思义就是跟踪连接。(引入问题过多不建议使用)
    • tcp_tw_recycle:顾名思义就是回收TIME_WAIT连接。(NAT模式下有问题,产生时间戳混乱,不建议使用)
    • tcp_tw_reuse:顾名思义就是复用TIME_WAIT连接。(作为连接的发起方(Client)有用(需网络稳定),作为被连接方基本没用)
    • tcp_max_tw_buckets:顾名思义就是控制TIME_WAIT总数。(治标不治本,限制tw队列大小系统日志会报「TCP: time wait bucket table overflow」)

    结论

    有时候,如果我们换个角度去看问题,往往能得到四两拨千斤的效果。前面提到的例子:客户端向服务端发起HTTP请求,服务端响应后主动关闭连接,于是TIME_WAIT便留在了服务端。这里的关键在于主动关闭连接的是服务端!在关闭TCP连接的时候,先出手的一方注定逃不开TIME_WAIT的宿命,套用一句歌词:把我的悲伤留给自己,你的美丽让你带走。如果客户端可控的话,那么在服务端打开KeepAlive,尽可能不让服务端主动关闭连接,而让客户端主动关闭连接,如此一来问题便迎刃而解了。

    相关文章

      网友评论

          本文标题:由nginx TW状态过多引发。。2018-01-17

          本文链接:https://www.haomeiwen.com/subject/oxcuoxtx.html