01 前言:超时不是异常,是场景题

“同样一段代码,压测 200 ms,上线 8 s。”
—— 99% 是因为场景变了,不是代码坏了。

下面把最易发生超时的 7 个现场拆开,每个都给可复制的解决代码,直接贴进项目就能用。

02 场景 ①:TCP 建联阻塞 —— 无连接超时

现场

  • 日志卡在 CONNECTING 30s 才抛异常

  • 重启后正常,网络闪断又复现

根因
默认没有 connectTimeout,OS 默认 75s!

一键解决

RequestConfig cfg = RequestConfig.custom()
        .setConnectTimeout(3000)   // ⭐ 3 秒硬超时
        .setSocketTimeout(5000)
        .build();

效果:闪断 3s 即失败,立即触发重试或熔断,不再傻等 75s


03 场景 ②:大结果集 —— 读取超时

现场

  • 小数据 200ms,大数据 8s

  • 线程池被打满,CPU 很低

根因
没有 socketTimeout一个包就能堵死线程

一键解决

.setSocketTimeout(5000)          // ⭐ 5 秒读超时
.setConnectionRequestTimeout(1000) // 从池拿连接 1s

进阶:分页 + 流式读取 setFetchSize(1000)把 8s 拆成 10 次 500ms


04 场景 ③:级联雪崩 —— 上游 > 下游

现场

  • 网关 10s 超时,业务服务 5s,DB 1s

  • 下游已熔断,上游还在等新连接 → 雪崩

根因
超时阶梯倒序

一键解决

  • 网关:10s

  • 业务:5s

  • 基础:3s

  • DB/Redis:1s

口诀上游 > 下游,防止“下游已死,上游傻等”。


05 场景 ④:重试风暴 —— 无限 while

现场

  • 失败率 5%,重试 3 次 → QPS × 3

  • 下游直接被打挂

根因
无限重试 + 无退避

一键解决(Resilience4j)

Retry retry = Retry.ofDefaults("srm",
    RetryConfig.custom()
        .maxAttempts(2)                 // ⭐ 最多 2 次(含首次)
        .waitDuration(Duration.ofSeconds(1)) // 1s 退避
        .retryExceptions(SocketTimeoutException.class) // 只重试网络
        .build());

效果:失败率 5% → 1%,QPS 不再爆炸


06 场景 ⑤:同步阻塞 —— 线程池打满

现场

  • supplyAsync 没有超时 → 线程永久阻塞

  • 重启后 1 分钟又满

根因
异步无超时,线程不释放。

一键解决(CompletableFuture + 超时)

CompletableFuture<List<Sku>> future = CompletableFuture
        .supplyAsync(() -> remoteClient.list(skuIds))
        .orTimeout(3, TimeUnit.SECONDS)   // ⭐ 3 秒硬超时
        .exceptionally(ex -> Collections.emptyList());

效果:3s 自动返回空列表,线程立即回池


07 场景 ⑥:分布式锁死等 —— 无尝试超时

现场

  • 锁被忘记 unlock,后续所有请求 永久阻塞

  • 重启才恢复

根因
lock.lock() 没有 尝试超时

一键解决(Redisson)

if (lock.tryLock(200, 10_000, TimeUnit.MILLISECONDS)) {
    // 200 ms 内拿不到就抛异常,不会死等
} else {
    throw new ZvosBaseException("系统繁忙,请稍后重试");
}

效果:200 ms 无锁立即失败,永不死等


08 场景 ⑦:结果集爆炸 —— 大对象传输

现场

  • 小数据 200ms,大数据 8s

  • 网络带宽打满,GC 频繁

根因
一次拉 10 万条,序列化耗时 7s!

一键解决

  1. 分页limit 1000

  2. 字段裁剪:只 select 需要的列

  3. 流式下载setFetchSize(1000) + ResponseEntity<StreamingResponseBody>

效果:8s → 0.5s,带宽下降 90%。


09 一句话总结(背下来)

“连接设 3s,读取设 5s,上游大于下游,重试 2 次带退避,异步加 orTimeout,锁只 tryLock,大结果分页裁字段——超时率直接压到 0.1%!”

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐