1. 连接池核心原理:OkHttp连接复用机制、Keep‑Alive策略、连接池数据结构

1.1 为什么需要连接池

每次 HTTP 请求都新建 TCP 连接会带来三次握手 + TLS 握手(HTTPS)的开销,延迟可达 50‑200ms。连接池通过复用已建立的连接消除这些握手成本,同时降低服务端连接数,减少资源消耗。

1.2 连接复用机制

OkHttp 的 ConnectionPool 管理所有空闲的 RealConnection。当发起请求时,ExchangeFinder 按以下优先级寻找可用连接:

  1. 查找当前 RealConnection 是否已被占用的连接(同一 host/port)。
  2. 从连接池的空闲队列中按 LRU 策略取出一个连接。
  3. 若未找到,则新建连接并注册到池中。

复用成功后,请求直接复用底层 Socket,无需重复 TCP/TLS 握手。

1.3 Keep‑Alive 策略

HTTP 1.1 默认使用 Connection: keep-alive。OkHttp 通过两个参数控制空闲连接存活:

参数默认值说明
maxIdleConnections5每 host 最大空闲连接数,超过则逐出最久未使用的连接
keepAliveDuration5 分钟空闲连接的最大存活时间,超过则被关闭

当连接空闲超过 keepAliveDuration 且未被复用时,ConnectionPool 的清理线程会将其关闭并移出队列。

1.4 连接池数据结构

核心数据结构如下:

  • Deque<RealConnection> connections – 按使用顺序存储所有连接(包括空闲和在用)
  • RouteDatabase routeDatabase – 记录连接失败的路由,避免频繁重试
  • cleanupRunnable – 后台任务,每隔 1 秒检查并清理过期空闲连接

每个 RealConnection 内部维护一个 List<Reference<StreamAllocation>> allocations,记录当前正在使用该连接的请求数量。当 allocations 为空时,连接变为空闲状态。

OkHttp 连接池数据结构与复用流程 连接池数据结构与连接复用流程 ConnectionPool 空闲 Deque<RealConnection> 连接 A (空闲) host: api.example.com 连接 B (空闲) host: cdn.example.com 在用 Deque<RealConnection> 连接 C (在用) allocations=1 连接 D (在用) allocations=2 请求复用流程 步骤 1:新请求到达 ExchangeFinder 查找 步骤 2:比较 host/port 与空闲连接匹配 步骤 3:复用连接 从空闲队列取出 步骤 4:移入在用队列 连接复用后,allocations 增加 1;请求完成后减少 1,当减为 0 时回到空闲队列。

1.5 配置连接池

通过 ConnectionPool 构造方法设置参数,并在 OkHttpClient.Builder 中注入:

// 创建连接池:最多保持 10 个空闲连接,空闲存活 30 秒
ConnectionPool pool = new ConnectionPool(10, 30, TimeUnit.SECONDS);

OkHttpClient client = new OkHttpClient.Builder()
    .connectionPool(pool)
    .build();

参数说明:

  • maxIdleConnections – 每 host 最大空闲连接数,避免短连接过多导致内存泄漏
  • keepAliveDuration – 空闲连接超时时间,建议根据应用网络行为调整(如 30s‑120s)

1.6 调试验证连接复用

添加 EventListener 观察连接创建和复用事件:

class ConnectionInspector extends EventListener {
    @Override
    public void connectionAcquired(Call call, Connection connection) {
        System.out.println("Connection acquired: " + connection.socket().getRemoteSocketAddress());
    }

    @Override
    public void connectionReleased(Call call, Connection connection) {
        System.out.println("Connection released: " + connection);
    }

    @Override
    public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {
        System.out.println("New connection start: " + inetSocketAddress);
    }
}

OkHttpClient client = new OkHttpClient.Builder()
    .connectionPool(new ConnectionPool(5, 5, TimeUnit.SECONDS))
    .eventListener(new ConnectionInspector())
    .build();

连续发起两次相同 host 的请求,观察日志:

  • 第一次:New connection start → Connection acquired
  • 第二次:直接输出 Connection acquired(复用),无 connectStart

1.7 性能提升数据

场景无连接池(每次新建)有连接池(复用)提升比例
同一 host 连续 10 次请求(HTTP)~450ms~80ms约 82% 延迟降低
同一 host 连续 10 次请求(HTTPS)~1.8s~200ms约 89% 延迟降低
服务器连接数(压力测试)瞬时 100 个连接压到 5‑10 个连接资源消耗降低 90%+

实际收益取决于 Keep‑Alive 时长和请求频率;建议在生产环境中使用默认值(5 个空闲 / 5 分钟),然后根据压测调整。

关键结论:连接池通过复用 TCP/TLS 连接,大幅降低延迟和服务器资源消耗。默认配置适用于多数场景,但高并发短连接业务(如实时推送)应缩短 keepAliveDuration 并增大 maxIdleConnections。