TCP_USER_TIMEOUT 是什么
TCP_USER_TIMEOUT 是 Linux 提供的套接字选项,单位毫秒。设置后,只要已经发出的数据在指定时间内没有收到确认,内核就认定连接不可用并主动断开,把对端假死、链路黑洞这类问题从「无期限等待」变成「确定时长后失败」。它与 SO_KEEPALIVE 的观测对象不同:keepalive 探测的是空闲连接,USER_TIMEOUT 管的是已有未确认数据的连接。
基本信息
| 适用平台 | Linux(setsockopt 选项,不是全局 sysctl 参数) |
|---|---|
| 单位 | 毫秒 |
| 典型调用 | setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &ms, sizeof(ms)) |
| 生效范围 | 仅当前套接字,需要应用自己设置 |
| 与 keepalive 的分工 | keepalive 管空闲连接,USER_TIMEOUT 管未确认数据 |
典型使用场景
- 长连接网关:对端进程卡死但 TCP 连接仍在时,避免请求线程被无限占住
- 数据库驱动:连接池里的连接遇到网络中断后能及时失效,而不是长期挂起
- 消息队列客户端:降低断网期间消息发送线程的阻塞时间
- 跨机房调用:链路劣化时让请求快速失败,把重试交给上层
- 代理与边车:在连接假死时及时释放文件描述符与内存
设置与观察
设置与观察
int ms = 30000;
setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &ms, sizeof(ms)); // 30 秒内未确认即断开
/* 观察:连接是否因超时被断开 */
/* ss -tin dst :3306 | grep -E 'retrans|timer' */超时覆盖的故障
| 故障形态 | 内核行为 | 应用需要做什么 |
|---|---|---|
| 对端进程卡死不再读 | 发送缓冲填满后无确认,超时后断开 | 捕获连接错误并重建连接 |
| 链路黑洞持续丢包 | 重传直至超过 USER_TIMEOUT | 快速失败并把重试交给上层 |
| 对端主机断电 | 无 ACK,超时后释放连接 | 清理连接池中的失效连接 |
tcp_keepalive_time 等内核参数只作用于空闲连接,不能替代 USER_TIMEOUT。连接上还有未确认数据时,keepalive 探测不会介入,这也是不少「连接假死」问题的根源。
取值要先测量链路的正常与抖动往返时间,再结合业务可容忍的最长等待来定;设得太短会在网络抖动时误断正常连接,反而制造故障。