长连接和连接超时是两个层级的概念,前者管连接复用、后者管单次请求,默认超时时间从几十秒到几分钟不等,具体取值取决于业务场景和技术栈,没有统一答案,但可以按规律推算。
长连接超时时间怎么设置:先分清三个"超时"
很多人把"连接超时"和"长连接超时"混在一起调,结果Nginx配了一大堆参数,后端还是报错,行业共识认为,绝大多数超时问题不是参数不够,而是没分清自己在调哪一层。
连接超时、读超时、长连接超时分别管什么
这三个词经常出现在同一份配置里,但作用完全不同:
- 连接超时:从客户端发起TCP握手,到服务器接受连接所等待的时间,超过就报
connect timed out。 - 读超时:连接建立后,客户端等待服务器返回响应数据的最长时间,超过就报
read timed out。 - 长连接超时:空闲连接在池子里存活的最长时间,超过后连接被关闭或回收,和单次请求无关。
举个例子:你用JDBC连MySQL,connectTimeout管的是网络层握手,socketTimeout管的是SQL执行后等结果的时间,而连接池的maxLifetime管的是这条连接最多活多久,三者独立生效,互不干扰。
常见中间件的默认值参考
| 中间件 | 连接超时默认值 | 读超时默认值 | 长连接空闲超时 |
|---|---|---|---|
| Nginx | 60s | 60s | 75s |
| Tomcat | 20s | 20s | 默认不主动断开 |
| MySQL JDBC | 0(无限) | 0(无限) | 8小时 |
| Redis客户端 | 2s | 2s | 不限制 |
默认值并不等于合理值,Nginx的75秒长连接空闲超时在服务端渲染时代够用,但在移动端弱网环境下,这个数字明显偏长。
服务器连接超时和读超时:请求链路里的两把尺子
把一次HTTP请求拆开看,超时风险点集中在两个阶段:建连阶段和响应阶段,国内云服务商在SLB(负载均衡)层通常会配置更短的连接超时,比如简米云SLB默认空闲超时60秒,这张表在控制台就能看到。

三次握手阶段:connect timeout
这个阶段最容易出问题的是跨机房调用,客户端配置了3秒连接超时,但服务器在北京、客户端在广州,碰上网络抖动,TCP重传还没结束,客户端就已经放弃了,实测经验是:同机房连接超时建议1-2秒,跨地域3-5秒。
排查时可以这样验证:
curl -v --connect-timeout 5 https://api.example.com
如果连续多次都在5秒边界附近失败,说明网络路径确实不稳,而不是参数问题。
数据传输阶段:read timeout
连接建立了,但服务器处理慢,客户端不能无限等,这里的核心矛盾是:读超时设置太短,慢接口会被频繁误杀;设置太长,线程资源被长时间占用。
一个可行的思路是区分接口类型,查询类接口读超时2-3秒,写操作或批量导出类接口放宽到10-30秒,不要一套参数走天下。
要观察实际耗时分布,可以用:
curl -w "time_total: %{time_total}s\n" -o /dev/null https://api.example.com
多跑几次,看p95或p99的值,再决定超时阈值。
长连接超时时间设置多少合适:从Nginx到Java后端
长连接超时设置,本质是平衡三个成本:连接复用的收益、空闲占用的资源、以及两端超时不一致带来的断连风险。
Nginx keepalive_timeout配置
Nginx中keepalive_timeout管的是客户端(通常是浏览器)到Nginx的长连接空闲时间,它有两个值:
keepalive_timeout 65s;
只写一个值表示两端都用65秒,写两个值则第一个负责客户端连接,第二个负责服务器端连接。注意这里的单位是秒,不是毫秒。
keepalive_requests要同步调大,默认100次请求就会强制断开连接,即时timeout设得再大,请求数到了照样断。
Java HTTP客户端连接池超时
Java生态里,Apache HttpClient和OkHttp是主流选择,Apache HttpClient 4.5+的连接池配置:
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(200); cm.setDefaultMaxPerRoute(50);

连接池的validateAfterInactivity参数设为2000毫秒,意思是空闲超过2秒的链接,取用前额外做一次状态检查。这个参数解决的就是"服务器端先关了连接,客户端不知道"的经典问题。
OkHttp则更简洁:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(5, 10, TimeUnit.MINUTES))
.build();
OkHttp的ConnectionPool空闲超时推荐5-10分钟,因为OkHttp有完善的连接复用机制,短于5分钟会频繁重建TCP连接,在TLS握手场景下尤其费时。
Netty和Spring Boot中的IdleStateHandler
服务端组件建议用Netty的IdleStateHandler:
ch.pipeline().addLast(new IdleStateHandler(60, 0, 0));
第一个参数是读空闲超时,60秒内没读到数据就触发IdleStateEvent,在userEventTriggered里关闭连接。服务端主动断开比干等TCP超时回收要快得多,能显著减少僵尸连接占用。
Spring Boot内嵌Tomcat的配置路径是application.yml:
server:
tomcat:
connection-timeout: 5000
keep-alive-timeout: 30s
max-keep-alive-requests: 100
这里connection-timeout单位是毫秒,keep-alive-timeout单位带s后缀,混着写容易错。
客户端主动断开和服务器主动断开:谁先断谁被动
长连接超时怕的不是断开,怕的是单方面断开,服务器把连接关了,客户端不知道,下次从池子里取出这条连接去发请求,轻则多一次重试,重则报错污染调用链。
TCP keepalive和HTTP keep-alive的区别
TCP层有个keepalive机制,默认是关闭的,开启后每7200秒(2小时)探测一次,Linux下可以调整:
net.ipv4.tcp_keepalive_time=1200 net.ipv4.tcp_keepalive_intvl=30 net.ipv4.tcp_keepalive_probes=3
把探测间隔缩短到20分钟,可以在不依赖业务心跳的情况下,让系统层先感知到断线。
TCP keepalive解决的是物理链路是否存活,HTTP keep-alive解决的是应用层连接能否复用

,前者是保险丝,后者是业务策略,两者不冲突。
心跳机制是双方的事
业务层面的心跳,不能只靠一端发,客户端定时发送ping,服务器收到后更新最后活跃时间,并返回pong,服务器侧的定时任务检查所有连接的最后活跃时间,超过阈值就主动close。
业界常用的心跳周期是:心跳间隔设为服务端超时时间的1/3,比如服务器长连接超时设90秒,客户端心跳就每30秒发一次,这样即使丢包重试,也能在超时窗口内补上一跳。
不建议把长连接超时时间设得太长,据统计,多数生产环境的长连接空闲超时集中在30秒到5分钟这个区间,太长的值只会让连接池里的死连接变多。
Q&A:关于长连接超时和连接超时的常见疑问
问:长连接超时后客户端怎么感知?
客户端在取用连接时会触发一次validateAfterInactivity检查,或者直接发请求后收到Connection reset异常,在高并发下,最常见的是服务端主动关闭后客户端第一次请求直接失败,然后连接池自动重建连接,第二次请求就正常了,这种情况不需要过度处理,只要业务有重试机制即可。
问:设置很短的长连接超时能否节省服务器资源?
能省一部分内存和文件描述符,但代价是频繁建立TCP连接,在HTTPS场景下意味着更多的TLS握手成本。多数情况下,把连接池的失效检查参数调好,比缩短长连接超时更有效,Nginx官方文档也指出,keepalive_timeout设置过短会导致吞吐量明显下降。
问:长连接和WebSocket超时行为一致吗?
行为机制一致,但默认值差异很大,WebSocket继承HTTP的握手建立连接,但之后不受keepalive_timeout管理,由proxy_read_timeout(在Nginx反代场景下)控制。WebSocket的代理超时建议设到300秒以上或直接留空,因为业务消息的间隔不可预测,用一个近似心跳的固定值容易误杀正常连接,国内云服务商的WebSocket接入通常也有独立的空闲超时设置,需要在控制台单独配置。