不能在 try 块里做高耗时网络 IO,因为这会将阻塞操作与异常处理耦合,放大延迟、掩盖问题;应将网络调用移出 try,仅用其解析响应体,或改用异步非阻塞方式,并通过前置校验和统一异常处理机制优化。

不能在 try 块里做高耗时网络 IO,不是因为 try-catch 本身慢,而是它常被误用在同步临界区或关键路径中,把异常处理逻辑和阻塞操作绑在一起,放大延迟、掩盖问题、拖垮吞吐量。
把网络调用移出 try-catch 保护范围
try-catch 只应包裹真正可能抛异常且需立即响应的语句,比如 JSON 解析、类型转换;而 HTTP 请求、RPC 调用、Redis 查询等属于外部依赖操作,本身耗时长、失败率高,不该放在需要原子性或低延迟保障的代码块里:
- ✅ 正确做法:先完成网络调用,拿到结果后再用 try 解析响应体(如
response.getBody()) - ❌ 错误做法:把
restTemplate.getForObject(...)整个包进 try,一旦超时或连接拒绝,整个 try 块卡住,锁还可能被持着(如果在 synchronized 内) - 若必须捕获网络异常,应在网关层、Feign 客户端拦截器或 Resilience4j 的 fallback 中统一处理,而非业务逻辑内硬编码 try
用异步非阻塞方式替代同步调用
Java NIO 或 Netty 支持异步 HTTP 客户端(如 WebClient、OkHttp 的 Callback 模式),让线程不等待响应,而是注册回调或返回 CompletableFuture:
- WebClient 发起请求后立即返回
Mono<String>,后续用onErrorResume处理失败,不阻塞当前线程 - 避免使用
RestTemplate同步阻塞调用,尤其在 Spring WebFlux 或高并发服务中 - 若用 OkHttp,优先选
enqueue()而非execute()
提前校验 + 快速失败,减少进 try 的概率
很多网络异常其实是可预判的,靠条件检查比靠 catch 更轻量:
立即学习“Java免费学习笔记(深入)”;
- 调用前检查参数合法性:
if (userId == null) throw new IllegalArgumentException("user id required");,避免因空值触发下游 400 或 NPE - 对缓存键做格式校验(如正则匹配 UUID),防止非法输入打到 Redis 或 DB 导致超时
- 用
health check或connect timeout配置主动规避已知不可达服务,而不是等 read timeout 触发异常
catch 块里只做最简动作
一旦进了 catch,说明已经出问题,此时任何额外操作都会直接加到响应时间上:
- 禁止在 catch 中发起新网络请求(如发告警短信、查用户状态)——应交由异步消息队列或监控系统拉取
- 日志只记 traceId、错误类型、HTTP 状态码,不用
e.printStackTrace()或序列化整个 request 对象 - 不 sleep、不重试、不 new Thread,重试逻辑交给专门组件(如 Spring Retry、Resilience4j)在非临界路径执行


















