4.1 连接管理核心组件
4.1.1 连接管理类关系图
4.2 TCP连接建立过程
4.2.1 连接建立流程图
4.2.2 连接建立源码分析
// 源码路径: okhttp3/internal/connection/RealConnection.java
public void connect(int connectTimeout, int readTimeout, ...) {
// 1. 创建原始Socket
rawSocket = proxy.type() == Proxy.Type.DIRECT
? address.socketFactory().createSocket()
: new Socket(proxy);
// 2. 设置超时
rawSocket.setSoTimeout(readTimeout);
rawSocket.connect(new InetSocketAddress(route.socketAddress(), connectTimeout));
// 3. 建立TLS隧道(HTTPS场景)
if (route.address().sslSocketFactory() != null) {
upgradeToTls();
}
// 4. 协议协商(HTTP/1.1或HTTP/2)
if (protocol == Protocol.HTTP_2) {
startHttp2Connection();
}
}
private void upgradeToTls() {
// 1. 创建SSLSocket
sslSocket = (SSLSocket) address.sslSocketFactory()
.createSocket(rawSocket, address.url().host(), address.url().port(), true);
// 2. 配置TLS参数
ConnectionSpec connectionSpec = route.connectionSpec();
connectionSpec.apply(sslSocket, isTlsFallback);
// 3. 开始TLS握手
sslSocket.startHandshake();
// 4. 验证主机名
if (!address.hostnameVerifier().verify(address.url().host(), sslSocket.getSession())) {
throw new SSLPeerUnverifiedException("Hostname verification failed");
}
// 5. 获取握手信息
handshake = Handshake.get(sslSocket.getSession());
}
4.3 连接复用机制
4.3.1 连接复用判断逻辑
// 源码路径: okhttp3/internal/connection/RealConnection.java
public boolean isEligible(Address address, @Nullable Route route) {
// 1. 连接已达并发流限制(HTTP/2)
if (allocations.size() >= allocationLimit || noNewStreams) {
return false;
}
// 2. 非HTTP/2需完全匹配Address
if (!this.route.address().equals(address)) {
return false;
}
// 3. 路由必须匹配
if (route != null && !this.route.equals(route)) {
return false;
}
// 4. HTTP/2需验证主机名证书
if (http2Connection != null) {
return address.hostnameVerifier().verify(address.url().host(), handshake().peerPrincipal());
}
return true; // 满足复用条件
}
4.3.2 连接获取流程
4.4 连接池管理
4.4.1 连接池数据结构
// 源码路径: okhttp3/internal/connection/ConnectionPool.java
public final class ConnectionPool {
// 空闲连接队列(双向链表)
private final Deque<RealConnection> connections = new ArrayDeque<>();
// 清理任务
private final Runnable cleanupRunnable = () -> {
while (true) {
// 执行清理
}
};
}
4.4.2 连接回收策略
// 源码路径: okhttp3/internal/connection/ConnectionPool.java
void put(RealConnection connection) {
// 1. 检查是否正在清理
if (cleanupRunning) {
// 启动清理线程
}
// 2. 加入连接池
connections.add(connection);
}
synchronized void recycle(RealConnection connection) {
// 1. 从活跃连接移除
connection.allocations.remove(connection);
// 2. 加入空闲队列
connections.add(connection);
}
4.5 连接池清理机制
4.5.1 清理算法流程图
4.5.2 清理源码实现
// 源码路径: okhttp3/internal/connection/ConnectionPool.java
long cleanup(long now) {
int idleCount = 0;
RealConnection longestIdle = null;
long longestIdleDuration = Long.MIN_VALUE;
// 1. 统计空闲连接
for (RealConnection connection : connections) {
if (connection.isIdle()) {
idleCount++;
long idleDuration = now - connection.idleAtNanos;
if (idleDuration > longestIdleDuration) {
longestIdle = connection;
longestIdleDuration = idleDuration;
}
}
}
// 2. 执行清理决策
if (longestIdleDuration >= keepAliveDurationNs || idleCount > maxIdleConnections) {
connections.remove(longestIdle);
closeQuietly(longestIdle.socket()); // 关闭连接
return 0; // 立即再次检查
}
// 3. 计算下次清理时间
return keepAliveDurationNs - longestIdleDuration;
}
4.6 HTTP/2多路复用
4.6.1 HTTP/2连接建立
4.6.2 HTTP/2连接复用
// 源码路径: okhttp3/internal/http2/Http2Connection.java
public Http2Stream newStream(
List<Header> requestHeaders,
boolean out,
boolean in) throws IOException {
// 1. 创建新流
Http2Stream stream;
int associatedStreamId;
synchronized (writer) {
synchronized (this) {
// 2. 分配流ID
int nextStreamId = nextStreamId;
if (nextStreamId > Integer.MAX_VALUE / 2) {
shutdown(REFUSED_STREAM);
}
this.nextStreamId = nextStreamId + 2;
stream = new Http2Stream(nextStreamId, this, out, in, requestHeaders);
}
// 3. 发送HEADERS帧
writer.headers(stream.getId(), requestHeaders);
}
return stream;
}
4.7 连接生命周期监控
4.7.1 事件监听接口
// 源码路径: okhttp3/EventListener.java
public abstract class EventListener {
// 连接开始
public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {}
// TLS握手开始
public void secureConnectStart(Call call) {}
// TLS握手结束
public void secureConnectEnd(Call call, @Nullable Handshake handshake) {}
// 连接建立完成
public void connectEnd(Call call, InetSocketAddress inetSocketAddress,
Proxy proxy, @Nullable Protocol protocol) {}
// 连接被复用
public void connectionAcquired(Call call, Connection connection) {}
// 连接释放
public void connectionReleased(Call call, Connection connection) {}
}
4.7.2 连接监控实现
OkHttpClient client = new OkHttpClient.Builder()
.eventListener(new EventListener() {
@Override
public void connectionAcquired(Call call, Connection connection) {
log("Connection acquired: " + connection.hashCode());
}
@Override
public void connectionReleased(Call call, Connection connection) {
log("Connection released: " + connection.hashCode());
}
@Override
public void connectEnd(Call call, InetSocketAddress inetSocketAddress,
Proxy proxy, Protocol protocol) {
log("Connected via: " + protocol);
}
})
.build();
4.8 连接优化策略
4.8.1 DNS优化
// 自定义DNS解析器
public class CustomDns implements Dns {
private static final long CACHE_DURATION = 5 * 60 * 1000; // 5分钟缓存
private final Map<String, DnsCacheEntry> cache = new ConcurrentHashMap<>();
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
// 1. 检查缓存
DnsCacheEntry cached = cache.get(hostname);
if (cached != null && !cached.isExpired()) {
return cached.addresses;
}
// 2. 系统DNS解析
List<InetAddress> addresses = Dns.SYSTEM.lookup(hostname);
// 3. 缓存结果
cache.put(hostname, new DnsCacheEntry(addresses));
return addresses;
}
private static class DnsCacheEntry {
final List<InetAddress> addresses;
final long expireTime;
DnsCacheEntry(List<InetAddress> addresses) {
this.addresses = addresses;
this.expireTime = System.currentTimeMillis() + CACHE_DURATION;
}
boolean isExpired() {
return System.currentTimeMillis() > expireTime;
}
}
}
4.8.2 HTTP/2参数优化
OkHttpClient client = new OkHttpClient.Builder()
.connectionSpecs(Arrays.asList(
new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.http2Settings(new Settings.Builder()
.set(Settings.INITIAL_WINDOW_SIZE, 16777216) // 16MB窗口
.set(Settings.MAX_CONCURRENT_STREAMS, 100) // 最大并发流
.build())
.build()))
.build();
4.9 连接问题排查
4.9.1 常见连接问题
-
连接泄漏:
- 现象:应用运行一段时间后无法发起新连接
- 原因:未关闭ResponseBody
- 解决:确保使用try-with-resources或手动关闭
-
TLS握手失败:
- 现象:SSLHandshakeException
- 原因:协议/加密套件不匹配
- 解决:调整ConnectionSpec配置
-
连接超时:
- 现象:ConnectTimeoutException
- 原因:网络问题或服务器不可达
- 解决:增加连接超时时间,检查网络
4.9.2 连接诊断工具
// 启用详细日志
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
// 输出连接池状态
ConnectionPool pool = client.connectionPool();
System.out.println("空闲连接数: " + pool.idleConnectionCount());
System.out.println("总连接数: " + pool.connectionCount());
本章小结
-
连接建立过程:
- DNS解析获取IP地址
- TCP三次握手建立连接
- TLS握手加密通信(HTTPS)
- 协议协商(HTTP/1.1或HTTP/2)
-
连接复用机制:
- 相同Address可复用连接
- HTTP/2支持多路复用
- 空闲连接最大5个,存活5分钟
-
连接池管理:
- 使用双端队列存储连接
- 定时清理空闲连接
- 自动回收释放的连接
-
HTTP/2特性:
- 二进制分帧传输
- 多路复用减少连接数
- 头部压缩减少开销
- 服务器推送提升性能
-
性能优化策略:
- DNS缓存减少解析时间
- 调整HTTP/2窗口大小
- 监控连接生命周期
- 合理配置连接池参数
在下一章中,我们将深入分析HTTP/2多路复用实现,包括二进制分帧、流控制、优先级调度等核心机制。