Socket超时发生时底层TCP连接会发生什么?HTTP1与HTTP2表现有何差异?
前置场景
假设我们有一个类似Spring RestTemplate 的Java Web客户端,可以对其配置读取超时:
RestTemplateBuilder() .setReadTimeout(Duration.ofMillis(1000)) .build()
发起HTTP调用后如果服务器响应过慢,会抛出异常:java.net.SocketTimeoutException
1. 超时发生时底层TCP连接的变化
Linux下poll系统调用触发超时的时候,内核不会主动关闭TCP连接,只会返回超时的事件通知,将控制权交回用户态进程,后续的连接处理逻辑完全由上层应用实现决定:
- 如果你使用的是默认JDK
HttpURLConnection作为RestTemplate的底层客户端:触发SocketTimeoutException后,JDK会主动调用close()关闭套接字,后续服务端发送的响应数据包到达时,内核会直接回复RST报文丢弃,不会向上层转发。 - 如果你使用的是Apache HttpClient、OkHttp这类支持连接池的客户端实现:读取超时发生后,客户端会判定该连接存在未确定的残留数据,为了避免后续复用连接时出现请求/响应串位的问题,会将该连接从连接池中移除并主动关闭,不会再复用该连接。
如果上层应用没有主动关闭连接,后续服务端的响应数据会正常存入内核的套接字接收缓冲区,直到缓冲区满后服务端停止发送,但是上层应用已经不会再读取该缓冲区的数据,会造成资源泄漏。
2. HTTP2与HTTP1的超时行为差异
HTTP1.x的请求-响应模型是单流串行,同一个TCP连接同一时间只能处理一个请求,因此超时行为是连接粒度的:
- 单个请求读取超时会直接销毁整个TCP连接,不会再复用。
HTTP2引入了多路复用机制,单个TCP连接上可以并行跑多个独立的Stream(每个Stream对应一个请求-响应对),因此超时行为是流粒度的:
- 单个请求读取超时只会向服务端发送
RST_STREAM帧关闭对应Stream,不会影响同连接上其他正在处理的请求,整个TCP连接还可以继续复用。 - 只有连接级别的空闲超时、握手失败、协议错误等全局异常场景下,才会关闭整个TCP连接。
- 超时Stream后续到达的响应帧会被客户端直接丢弃,不会影响其他Stream的正常数据处理。
内容的提问来源于stack exchange,提问作者Maciej Mościcki
相关产品推荐
相关产品推荐

