网络超时导致程序崩溃?别慌,先看看这几个地方

前两天同事老张急得满头大汗,说生产环境一个服务突然挂了,日志里全是“连接超时”,重启之后几分钟又崩。查来查去,最后发现不是网络真断了,而是程序对超时处理太脆弱,一卡就直接崩溃。

超时不等于网络断了

很多人一看到“timeout”就以为是网络问题,其实更多时候是程序没做好防御。比如调用第三方API,对方服务器稍微慢一点,你的请求卡住30秒,如果没设超时时间,整个线程就被占着,用户刷新几次,线程池耗尽,服务直接瘫痪。

就像你去银行办事,窗口只有一个,前面有人一直不走,后面队伍越排越长,最后干脆关门停业。这不是银行倒闭了,是流程设计有问题。

常见的崩溃场景

最常见的就是HTTP客户端没设超时。比如用Java的OkHttp,默认情况下 connectTimeout 是10秒,但如果你手动改成了0(无限等待),那只要一次网络抖动,请求堆积,内存暴涨,JVM直接OOM。

OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(0, TimeUnit.SECONDS) // 危险!
.readTimeout(0, TimeUnit.SECONDS) // 更危险!
.build();

正确的做法是明确设置合理值:

OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();

数据库连接也要防超时

有时候程序连数据库,网络延迟高一点,连接池里的连接全被占满,新请求进不来。这时候不只是接口变慢,可能直接抛异常,触发未捕获异常导致进程退出。

比如MySQL连接串里,加上这些参数:

jdbc:mysql://localhost:3306/mydb?connectTimeout=5000&socketTimeout=30000&autoReconnect=true

connectTimeout 控制握手阶段最长等多久,socketTimeout 控制读写数据的等待时间。别小看这两个参数,关键时刻能保住服务。

别让异常逃出主线程

有些程序员写异步任务,比如用线程池处理定时拉取数据,结果网络超时抛了IOException,没try-catch,异常一路冒泡到线程池的默认处理器,线程死掉,任务不再执行。

更糟的是,有些框架会因为未捕获异常直接关闭整个应用。这种情况在Spring Boot里也出现过,特别是用了@Scheduled注解但没配TaskExecutor的时候。

加个熔断和降级更稳

光设超时还不够。万一依赖的服务连续超时,不如暂时“放弃治疗”,直接走本地缓存或返回默认值。Hystrix、Resilience4j这类工具就是干这个的。

比如用Resilience4j设置一个超时熔断规则:

TimeoutConfig config = TimeoutConfig.custom()
.timeoutDuration(Duration.ofSeconds(3))
.build();

超过3秒就直接失败,不等,避免资源被长期占用。

监控得跟上

线上服务一定要有超时告警。比如Prometheus抓取接口响应时间,超过2秒就发通知。别等到用户投诉了才去查。

还有日志里要记录清楚是哪种超时:连接超时、读超时、写超时?来源是哪个IP?目标服务是什么?信息越细,排查越快。

老张后来加了超时配置,又上了熔断,现在就算第三方接口抽风,自家服务也能稳住。程序不怕出问题,怕的是出了问题没准备。”}