近日平稳运行了将近4年的发号器突然出现问题,在元旦0分的时候出现短暂的性能下降,导致发号失败率飙高到一个不可接收的值,哎,意外总是发生在你想不到的地方。
这几天赶紧和小伙伴们赶紧追查原因,制定改造方案,下面记录一下分析和定位问题的过程,以便后期查阅,并不在同一个地方跌倒两次。
一、分析过程
1、现象
现象是业务所用的uuid服务的6052端口出现性能下降,导致成功率下降。
2、日志
出问题第一反应就是去看日志,我们HAProxy的日志级别是notice,具体的报错如下
Jan 1 00:00:17 localhost haproxy30864: Server uuid6051/s2 is DOWN, reason: Socket error, info: "Cannot assign requested address", check duration: 0ms. 1 active and 1 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
Jan 1 00:00:22 localhost haproxy30873: Server uuid6051/s2 is UP, reason: Layer4 check passed, check duration: 268ms. 2 active and 1 backup servers online. 0 sessions requeued, 0 total in queue.
很简单,HAproxy认为rs的s2 down了,但是我们通过其他手段发现,s2其实并没有任何问题,这个报错应该不是s2真down引发,而是HAproxy认为s2 down引发,所以问题根源还要在HAproxy所在的服务器上继续找。
3、架构
简单描述一下我们的集群架构,我们使用4台物理服务器,每2台一组。每台服务器上部署2个实例,分别为6051和6052端口,前面通过HAproxy来对后端的rs进行健康检查和负载均衡。2台服务器上的HAproxy之间通过keepalive保持高可用。
4、寻找线索
由于当时现场保存不全,各种信息已经无法重放。(ps:凸显监控的重要性啊,没有历史数据和现场回放追问题太痛苦了)只好进行现场monitor, 在各种横向对比的过程中我们发现某一台服务器的cpu idle在案发时刻有较为明显的变化,再进一步发现这台服务器的time-wait非常的高,其他服务器的仅在200-300,这台服务器的可以达到 2w-3w,看来当天晚上的问题和高time-wait有关系了。
5、定位问题
高time-wait的情况一般发生在短连接的情况下,所以我们决定通过压测重现现场,以便判断。最终通过压测重现了当天的现象,HAproxy都报错信息完全一致,至此基本可以判定当天的报错就是由于time-wait过高引发的。
另外,在和开发的沟通中发现,有用长连接的也有用短连接的,而使用短连接的业务出现报错的情况更为突出,这也侧面验证了我们的判断。
二、优化过程
如果要优化,那么要先说说tcp的各种状态转化和连接建立。
本例问题的根本原因就在于由于短连接主动关闭,导致HAproxy也会主动关闭其和后端的链接,导致HAproxy本地出现大量的time- wait状态,而linux建立tcp连接受限于port,time-wait状态会占用大批量的port,如果所有port都被占用之后,就无法在新建 立连接,也就会出现上面的报错。
至此,优化思路应该有了,其一,加大可用port端口,其二,加快time-wait状态回收。
1、增加可用port数
增加可用端口的方法比较简单,linux中由ip_local_port_range控制,默认一般为32768到61000,可以通过如下命令修改
cat /proc/sys/net/ipv4/ip_local_port_rang
echo “1024 61000” > /proc/sys/net/ipv4/ip_local_port_rang
另外,由于tcp建立连接需要通过4元组确定,即srcip:srcport-dstip:dstport,故可以通过增加rs上uuid实例数,也就是增加新port数目来增加,每增加一个port即可以增加57000+的连接。
2、快速回收time-wait状态
time-wait状态,是在tcp关闭连接的时候的一个状态,它会在client端(发起关闭的端)存活2MSL(2分钟,maximun segment lifetime,最大分节生命期),在这个期间分配出去的port不会被复用,这样就会导致port资源的大量消耗也就导致了问题的出现。
“TCP 4次握手关闭连接流程图”
接下来说一下有那些参数对快速回收time-wait状态有效果
net.ipv4.tcp_tw_recyle
Enable fast recycling TIME-WAIT sockets. Default value is 1.It should not be changed without advice/request of technical experts
net.ipv4.tcp_tw_reuse
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
net.ipv4.tcp_fin_timeout
This specifies 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 attacks. In Linux 2.2, the default value was 180.
net.ipv4.tcp_max_tw_buckets
The maximum number of sockets in TIME_WAIT state allowed in the system. This limit exists only to prevent simple denial-of-service attacks. The default value of NR_FILE*2 is adjusted depending on the memory in the system. If this number is exceeded, the socket is closed and a warning is printed.
尤其是tcp_max_tw_buckets,这个是全局的限定,虽然tcp建立连接要依赖于4元组,socket的port可以服用,但是对于 tw buckets来说是按照合计计算的。一旦设定某一个数值之后,如果超过限定值,系统会立刻回收资源,并在message中记录一条warning。
localhost kernel: TCP: time wait bucket table overflow
三、总结
1、监控是最最重要的,没有“现场”,哪里谈“破案”
2、重现是第二重要的,万事不能靠猜,只有能重现才能验证问题并验证之后的解决方案
3、信息准确,只要在一个准确的信息上才能进行分析,信息不准确只会把你引到错误的路上