如此狗血的TCP close_wait

大家好,我是「云舒编程」,今天我们来聊聊最近遇到的线上出现大量close_wait导致服务不可用的问题。

文章首发于微信公众号:云舒编程

关注公众号获取: 1、大厂项目分享 2、各种技术原理分享 3、部门内推

一、问题

     服务A调用服务B,在服务A的机器上出现了大量的close_wait状态的TCP连接。

二、closed_wait

     根据TCP四次挥手,理论上close_wait是一个非常短暂的状态,对应到下图:当服务端接收到客户端的FIN并且回复ACK后服务端就会进入close_wait。然后该服务端继续发送FIN包后就会继续进入后续的流程,最终会正常关闭TCP连接。

     如果服务端出现了大量的close_wait那就证明没有进行正常的TCP关闭,也就是服务端最终没有调用close或者shutdown,导致最后一个FIN没有发出去。

IP异常

通过排查发现服务A处于closed_wait状态的对应的服务B的IP 都已经不在对方的服务列表中了。

同时同事反馈前天进行了一次压测,触发了下游的自动扩缩容。拿着这些IP跟运维确定,发现的确是前天扩容后又缩容了的IP。

三、分析

出现大量closed_wait的条件:

  1. 大量的短TCP链接
  2. 未正确关闭TCP(close或者shutdown)

前天压测满足了条件一,那就只剩下条件二了。

由于服务使用了连接池,猜测是不是这里导致的问题。连接池大致逻辑如下:

golang 复制代码
type ConnPool struct{
    poolName string
    connsMap map[string][]*net.Conn //key是对端ip+port,value是连接池列表
}

func (cli *TcpClient) doSend(ctx context.Context,sendByte []byte)([]byte,error){
    pool := getPool(TCP_POOL_NAME)

    //根据IP,PORT 分配conn ip port从服务注册中心获取
    conn := pool.Alloc(cli.ip,cli.port)

    //放回连接池
    defer pool.Put(conn)

    conn.Write()

    conn.Read()

    return 
}

发现该连接池的管理比较坑,使用被调用方的ip+port作为key进行存储。如果对方的服务下线了,那么从服务注册中心就再也无法获取该ip了,其对应的TCP连接就再也无法释放,并且未对连接做探活处理,从而导致TCP状态会永远停留在closed_wait状态。

以前为什么没有出现

     按照上述的连接池实现,只要下游的IP出现了变化,那么理论上我们的服务就会出现无法释放的closed_wait状态的连接才对。那这个问题应该早就暴露了才对?

     通过排查就发现了极其狗血的事情:下游服务的发布窗口在每周四的下午,我们的服务发布是在每周五的下午。通过狗血的发布窗口就把这个事情给自然解决了。

问题解决

  1. TCP连接设置keepalive
  2. 单独使用一个协程定时去检测连接是否可用
    • 读取到了io.EOF,这种就说明对端(服务端)关闭了这个连接,该连接可以释放了。
    • 拿ip、port 询问注册中心是否可用,不可用则关闭。

推荐阅读

1、原来阿里字节员工简历长这样

2、一条SQL差点引发离职

3、MySQL并发插入导致死锁

相关推荐
我不会插花弄玉12 小时前
6.传输层协议UDP【由浅入深-网络】
网络·网络协议·udp
时间的拾荒人12 小时前
Redis 主从复制详解:配置实操 + 原理机制
数据库·redis·面试
火云代理12 小时前
更换网络后为什么会退出登录?分清 IP、Cookie 与会话的关系
服务器·网络·网络协议·tcp/ip·php·ip
火云代理16 小时前
同一个网站,有的网络能打开、有的打不开?认识 IPv4 与 IPv6 的访问差异
服务器·网络·网络协议·tcp/ip·测试工具·php·状态模式
坤坤子吖17 小时前
Linux网络编程:TCP 粘包与半包问题,如何正确处理 TCP 流式数据?
linux·服务器·网络·笔记·网络协议·学习
火云代理19 小时前
Python 使用代理 IP 前,先弄清 Requests 的配置来源与验证方法
网络·python·网络协议·tcp/ip·ip
如夏倾城20 小时前
QUIC——HTTP/3 的唯一底层传输协议 及 0-RTT恢复
网络·网络协议·tcp/ip·安全·http
nice_lcj5201 天前
LRU 缓存入门:它是什么、怎么用、面试怎么手写
java·数据结构·缓存·面试·lru
聪明的笨猪猪2 天前
企业知识库AI架构选型:落地导向的架构决策与组件清单
经验分享·面试·架构·embedding·数据库架构
略懂而已2 天前
安装SSL后网站速度变慢?
网络·网络协议·ssl