错误信息:连接超时 (Connection timed out)
java.net.ConnectException: 连接超时 (Connection timed out)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:476)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:218)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:394)
at java.net.Socket.connect(Socket.java:606)
at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:303)
at sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:173)
at sun.net.NetworkClient.doConnect(NetworkClient.java:180)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:499)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:594)
at sun.net.www.protocol.https.HttpsClient.<init>(HttpsClient.java:263)
at sun.net.www.protocol.https.HttpsClient.New(HttpsClient.java:366)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.getNewHttpClient(AbstractDelegateHttpsURLConnection.java:207)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1167)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1061)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:193)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1375)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1350)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:264)
服务代码中使用原始方式创建http请求
URL url = new URL(generalUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
在使用 HttpURLConnection 或其他没有连接池管理的 HTTP 客户端时,频繁创建连接可能导致
SocketTimeoutException,主要原因如下:
1. 连接资源未复用,频繁创建连接
每次请求都会创建新的 TCP 连接(三次握手),请求结束后立即关闭连接(四次挥手)。
在高并发场景下,短时间内创建大量连接,系统资源(文件描述符、端口、线程等)可能耗尽。
2. 系统资源限制
操作系统对 每个进程能打开的文件描述符数量 有限制(Linux 默认是 1024)。
每个 TCP 连接占用一个文件描述符,当连接数超过限制时,新的连接无法创建,导致超时。
3. TIME_WAIT 状态堆积
TCP 协议规定,连接关闭后会进入 TIME_WAIT 状态(通常持续 30s~2min)。
如果频繁创建连接,大量连接处于 TIME_WAIT 状态,导致端口资源被占用,新连接无法建立。
4. DNS 解析瓶颈
没有连接池时,每次请求都要重新进行 DNS 解析,如果 DNS 服务器响应慢,也会导致连接超时。
5. 服务端限制
后端服务(如 Nginx、Tomcat)或负载均衡器对单个客户端的连接数有限制。
如果客户端频繁建立连接,可能被服务端限流或拒绝服务,从而引发超时。
线程状态
WAITING状态:38个(线程正在无限期等待某个条件)
TIMED_WAITING状态:23个(线程正在限时等待某个条件)
RUNNABLE状态:10个(正在执行或准备执行的可运行线程)
BLOCKED状态:0个(无线程因等待监视器锁而阻塞)
NEW状态:0个(没有新创建但未启动的线程)
TERMINATED状态:0个(没有已终止的线程)
TIME_WAIT 状态的由来与作用
TCP 是面向连接的可靠协议,在连接关闭时需要进行 四次挥手,确保数据完整传输。
1. 四次挥手流程:
客户端发送 FIN 表示结束发送数据。
服务端响应 ACK。
服务端发送 FIN 表示结束发送数据。
客户端响应 ACK。
2. TIME_WAIT 状态的含义:
当客户端发送最后一个 ACK 后,进入 TIME_WAIT 状态。
持续时间为 2MSL(Maximum Segment Lifetime),通常是 30秒到2分钟。
目的是:
确保最后一个 ACK 被服务端接收(若丢失需重传 FIN)。
防止旧连接的报文段被新连接误用。
为什么连接“关闭后未释放”?
TCP 连接关闭 ≠ 立即释放端口和资源
即使代码中调用了 close(),操作系统仍会将连接置于 TIME_WAIT 状态一段时间。
在这段时间内,该连接使用的本地端口(如 192.168.1.1:54321 -> example.com:80)仍被占用。
如果频繁发起新连接,可能导致:
本地端口耗尽(java.net.BindException: Permission denied)
新连接建立失败或超时(SocketTimeoutException)
HTTP 客户端库对比(OkHttpClient vs RestTemplate vs Apache HttpClient)
|
特性 |
OkHttpClient |
RestTemplate |
Apache HttpClient |
|
HTTPS 支持 |
✅ 默认支持 |
✅ 默认支持(需证书合法) |
✅ 默认支持(需证书合法) |
|
SSL 手动配置 |
⚠️ 需自定义证书时( ) |
⚠️ 需自定义证书时(依赖底层实现) |
⚠️ 需自定义证书时( ) |
|
连接池支持 |
✅ 原生支持(高效复用连接) |
❌ 无连接池(底层为 ) |
✅ 支持( ) |
|
异步请求支持 |
✅ 原生支持( + 回调) |
❌ 无原生异步(需自行封装线程池) |
❌ 无原生异步(需配合 ) |
|
响应式编程支持 |
✅ 可配合 RxJava、Kotlin Coroutines |
❌ 无 |
❌ 无 |
|
性能 |
⚡ 高(轻量级 + HTTP/2 支持) |
⚠️ 中等(依赖底层实现) |
⚠️ 中等(功能全面但较重) |
|
线程安全 |
✅ 全局单例设计 |
✅ 线程安全(但建议每个线程独立实例) |
✅ 线程安全(需正确管理连接池) |
|
Spring 集成 |
⚠️ 需手动封装(如 替换) |
✅ 原生支持(但已过时) |
✅ 支持(需配置 ) |
|
维护活跃度 |
✅ 活跃(Square 维护) |
⚠️ 已过时(Spring 推荐 ) |
✅ 活跃(Apache 基金会维护) |
|
易用性 |
✅ API 简洁(链式调用) |
⚠️ 配置稍复杂(需熟悉 Spring 生态) |
⚠️ 配置复杂(大量 Builder 模式) |
|
适用场景 |
移动端/高性能需求 |
传统 Spring 项目(逐步迁移) |
企业级复杂需求(如代理、重试机制) |
推荐使用哪种?
推荐顺序:OkHttpClient > WebClient(Spring 5+) > Apache HttpClient > RestTemplate
1231

被折叠的 条评论
为什么被折叠?



