OkHttp 架构设计详解

12 阅读7分钟

OkHttp 架构设计详解


一、整体架构总览

OkHttp 的核心设计可以用一张分层图来概括:

┌─────────────────────────────────────────────────────┐
│                  应用层 (Application)                  │
│          OkHttpClient / Request / Response            │
├─────────────────────────────────────────────────────┤
│                 调度层 (Dispatcher)                    │
│          同步执行 / 异步线程池调度                       │
├─────────────────────────────────────────────────────┤
│              拦截器链 (Interceptor Chain)               │
│  ┌───────────────────────────────────────────────┐  │
│  │  RetryAndFollowUpInterceptor (重试与重定向)      │  │
│  │  BridgeInterceptor        (桥接/请求头补全)      │  │
│  │  CacheInterceptor         (缓存)               │  │
│  │  ConnectInterceptor       (连接)               │  │
│  │  CallServerInterceptor    (网络请求执行)         │  │
│  └───────────────────────────────────────────────┘  │
├─────────────────────────────────────────────────────┤
│              连接层 (Connection)                      │
│    ConnectionPool / RealConnection / Route           │
├─────────────────────────────────────────────────────┤
│                I/O 层 (Okio)                         │
│          Source / Sink / Buffer                       │
└─────────────────────────────────────────────────────┘

二、核心组件

1. OkHttpClient — 全局配置中心

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .addInterceptor(loggingInterceptor)        // 应用拦截器
    .addNetworkInterceptor(networkInterceptor)  // 网络拦截器
    .connectionPool(new ConnectionPool())
    .cache(new Cache(cacheDir, cacheSize))
    .dns(Dns.SYSTEM)
    .build();

设计原则:一个应用只需创建一个实例(单例),内部共享连接池、线程池、缓存。

2. Request & Response — 不可变数据对象

// Request: 不可变,描述"要做什么"
Request request = new Request.Builder()
    .url("https://api.example.com/data")
    .header("Authorization", "Bearer token")
    .post(requestBody)
    .build();

// Response: 不可变头部 + 一次性消费的 body
Response response = client.newCall(request).execute();

3. Call — 请求的执行单元

         ┌──────────────┐
         │  Call (接口)   │
         └──────┬───────┘
                │
         ┌──────▼───────┐
         │   RealCall    │  ← 实际实现
         └──────┬───────┘
                │
        ┌───────┴────────┐
        │                │
   execute()        enqueue()
   (同步)            (异步)
// 同步
Response response = client.newCall(request).execute();

// 异步
client.newCall(request).enqueue(new Callback() {
    @Override public void onFailure(Call call, IOException e) { }
    @Override public void onResponse(Call call, Response response) { }
});

三、调度器(Dispatcher)— 并发控制

┌──────────────────────────────────────────┐
│              Dispatcher                   │
├──────────────────────────────────────────┤
│  maxRequests = 64        (最大并发数)      │
│  maxRequestsPerHost = 5  (每主机最大并发)   │
├──────────────────────────────────────────┤
│  readyAsyncCalls   (等待队列)              │
│  runningAsyncCalls (执行中-异步)            │
│  runningSyncCalls  (执行中-同步)            │
├──────────────────────────────────────────┤
│  executorService   (线程池)               │
│  ← CachedThreadPool (核心0,无上限,60s回收) │
└──────────────────────────────────────────┘

调度流程

enqueue(call)
    │
    ▼
runningAsyncCalls.size < 64  ──── 否 ───→ 放入 readyAsyncCalls
 且 sameHost < 5                              (等待)
    │ 是
    ▼
放入 runningAsyncCalls
    │
    ▼
executorService.execute(AsyncCall)
    │
    ▼
执行完毕 → 从running移除 → 扫描ready队列 → 提升符合条件的call

四、拦截器链(核心设计)— 责任链模式

这是 OkHttp 最精髓的设计。

整体链路

            用户自定义应用拦截器 (Application Interceptors)
                        │
           ┌────────────▼────────────┐
           │ RetryAndFollowUpInterceptor │  ← 重试 & 重定向 (最多20次)
           └────────────┬────────────┘
           ┌────────────▼────────────┐
           │    BridgeInterceptor     │  ← 补全请求头 / gzip / Cookie
           └────────────┬────────────┘
           ┌────────────▼────────────┐
           │    CacheInterceptor      │  ← HTTP 缓存策略
           └────────────┬────────────┘
           ┌────────────▼────────────┐
           │   ConnectInterceptor     │  ← 建立 TCP/TLS 连接
           └────────────┬────────────┘
                        │
            用户自定义网络拦截器 (Network Interceptors)
                        │
           ┌────────────▼────────────┐
           │  CallServerInterceptor   │  ← 真正发送请求,读取响应
           └─────────────────────────┘

责任链执行机制

// 核心代码简化
public class RealInterceptorChain implements Interceptor.Chain {
    private final List<Interceptor> interceptors;
    private final int index;  // 当前执行到哪个拦截器

    @Override
    public Response proceed(Request request) {
        // 创建下一个链节点
        RealInterceptorChain next = new RealInterceptorChain(
            interceptors, index + 1, request, ...);

        // 取出当前拦截器
        Interceptor interceptor = interceptors.get(index);

        // 调用当前拦截器,传入"下一个链"
        Response response = interceptor.intercept(next);

        return response;
    }
}
intercept(chain)                  intercept(chain)                intercept(chain)
┌─────────────┐               ┌─────────────┐               ┌──────────────┐
│ Interceptor1 │──proceed()──→│ Interceptor2 │──proceed()──→│ Interceptor3  │
│              │←──response───│              │←──response───│               │
│  前置处理     │               │  前置处理     │               │  实际网络请求  │
│  后置处理     │               │  后置处理     │               │               │
└─────────────┘               └─────────────┘               └──────────────┘

各拦截器职责详解

① RetryAndFollowUpInterceptor
请求进入
   │
   ▼
while (true) {
   │
   ├── 创建 ExchangeFinder (寻找可用连接)
   │
   ├── 调用 chain.proceed(request)
   │       │
   │       ├── 成功 → 检查是否需要重定向 (301/302/307/308)
   │       │            ├── 是 → followUpRequest, 重定向次数 < 20? → 继续循环
   │       │            └── 否 → 返回 response
   │       │
   │       └── 异常 → 判断是否可以恢复
   │                   ├── 连接未建立 / 幂等请求 → 重试
   │                   └── 不可恢复 → 抛出异常
   │
}
② BridgeInterceptor
请求方向 (补全请求头):
  ┌─────────────────────────────────────┐
   Content-Type   (根据 RequestBody)    
   Content-Length (根据 RequestBody)    
   Host           (从 URL 提取)         
   Connection: Keep-Alive             
   Accept-Encoding: gzip              
   Cookie         (从 CookieJar 读取)  
   User-Agent: okhttp/4.x.x          
  └─────────────────────────────────────┘

响应方向 (解压缩):
  如果响应是 gzip   GzipSource 解压  移除 Content-Encoding/Content-Length
③ CacheInterceptor
    请求进入
       
       
   Cache 中查找候选响应 (cacheCandidate)
       
       
  CacheStrategy.Factory 计算策略
       
       ├── networkRequest = null, cacheResponse = null
              返回 504 (强制缓存但无缓存)
       
       ├── networkRequest = null, cacheResponse != null
              直接返回缓存 (强缓存命中, Cache-Control: max-age)
       
       ├── networkRequest != null, cacheResponse = null
              走网络
       
       └── networkRequest != null, cacheResponse != null
               条件请求 (If-None-Match / If-Modified-Since)
               网络返回 304  合并缓存头部返回
               网络返回 200  更新缓存,返回新响应
④ ConnectInterceptor
// 核心:找到或创建一个可用连接
override fun intercept(chain: Chain): Response {
    val exchange = chain.call.initExchange(chain)  // 关键!
    return chain.proceed(request)
}
initExchange()
    │
    ▼
ExchangeFinder.findHealthyConnection()
    │
    ├── 1. 尝试复用当前 Call 已有的连接
    ├── 2. 从 ConnectionPool 中查找可复用连接
    ├── 3. 切换路由再从 Pool 查找
    └── 4. 创建新连接 (TCP握手 + TLS握手)
            │
            ▼
        放入 ConnectionPool
⑤ CallServerInterceptor
  写入请求头 (HTTP/1.1 或 HTTP/2 帧)
       │
       ▼
  写入请求体 (POST/PUT)
  ├── 普通写入
  └── Expect: 100-continue → 等服务器确认再发body
       │
       ▼
  刷新输出流 (flush)
       │
       ▼
  读取响应头
       │
       ▼
  构建 Response (包装响应体为延迟读取的 Source)
       │
       ▼
  返回 Response

五、连接管理

1. ConnectionPool(连接池)

┌─────────────────────────────────────────┐
│            ConnectionPool               │
├─────────────────────────────────────────┤
│  maxIdleConnections = 5                 │
│  keepAliveDuration  = 5 minutes         │
├─────────────────────────────────────────┤
│  connections: ConcurrentDeque<Connection>│
├─────────────────────────────────────────┤
│  cleanupRunnable (后台清理线程)            │
│  ← 定期扫描,清理超过keepAlive的空闲连接    │
│  ← 清理超过maxIdle的多余空闲连接           │
└─────────────────────────────────────────┘

2. 连接复用机制

新请求到来
    │
    ▼
遍历 ConnectionPool
    │
    ├── 匹配条件:
    │   ✓ 相同 host + port
    │   ✓ 连接未关闭
    │   ✓ 未超过最大并发流 (HTTP/2 可多路复用)
    │   ✓ TLS 证书匹配
    │
    ├── 命中 → 复用连接 (避免 TCP+TLS 握手开销)
    │
    └── 未命中 → 新建连接 → 放回池中

3. 连接建立流程

┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│   DNS     │────→│ TCP 握手  │────→│ TLS 握手  │────→│  HTTP     │
│  解析     │     │ (3次握手)  │     │(如果HTTPS)│     │ 请求/响应  │
└──────────┘     └──────────┘     └──────────┘     └──────────┘
     │                                  │
     ▼                                  ▼
 支持自定义DNS              支持证书锁定 (CertificatePinner)
 (实现Dns接口)              支持自定义TrustManager

六、HTTP/2 支持

┌─────────────────────────────────────────┐
│           HTTP/2 多路复用                 │
├─────────────────────────────────────────┤
│                                         │
│  一个 TCP 连接 ─┬─ Stream 1 (请求A)      │
│                ├─ Stream 2 (请求B)      │
│                ├─ Stream 3 (请求C)      │
│                └─ Stream N ...          │
│                                         │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐   │
│  │ HEADERS │ │  DATA   │ │SETTINGS │   │
│  │  Frame  │ │  Frame  │ │  Frame  │   │
│  └─────────┘ └─────────┘ └─────────┘   │
│                                         │
│  Http2Connection                        │
│    ├── Http2Writer (发送帧)              │
│    ├── Http2Reader (读取帧)              │
│    ├── streams: Map<Int, Http2Stream>   │
│    └── settings (流控/窗口大小)           │
└─────────────────────────────────────────┘

七、缓存架构

┌────────────────────────────────────────────┐
│               Cache                        │
├────────────────────────────────────────────┤
│  底层: DiskLruCache                        │
│  ├── Key = MD5(request.url)               │
│  ├── Entry.0 = 响应头 (metadata)           │
│  └── Entry.1 = 响应体 (body)              │
├────────────────────────────────────────────┤
│  缓存策略判断 (HTTP RFC 7234):              │
│  ├── Cache-Control: max-age, no-cache...  │
│  ├── Expires                              │
│  ├── ETag → If-None-Match                 │
│  ├── Last-Modified → If-Modified-Since    │
│  └── Age, Vary 等                         │
└────────────────────────────────────────────┘

八、完整请求流程图

client.newCall(request).enqueue(callback)
    │
    ▼
┌─────────────────┐
│   Dispatcher     │  ← 判断并发数,决定立即执行还是入队
│   调度           │
└───────┬─────────┘
        │ 线程池执行
        ▼
┌─────────────────┐
│ getResponseWith  │  ← 构建拦截器列表
│ InterceptorChain │
└───────┬─────────┘
        │
        ▼
┌─────────────────────────────────────────────────┐
│           Interceptor Chain                      │
│                                                  │
│  [用户应用拦截器]                                  │
│       ↓                                          │
│  RetryAndFollowUpInterceptor                     │
│       ↓                  ↑ 重试/重定向             │
│  BridgeInterceptor                               │
│       ↓ 补全请求头        ↑ gzip解压              │
│  CacheInterceptor                                │
│       ↓                  ↑ 写入缓存               │
│    ┌──┴──────┐                                   │
│    │有缓存命中│→ 直接返回                           │
│    │无缓存    │                                    │
│    └──┬──────┘                                   │
│  ConnectInterceptor                              │
│       ↓ 获取/创建连接                              │
│  [用户网络拦截器]                                  │
│       ↓                                          │
│  CallServerInterceptor                           │
│       ↓ 写请求          ↑ 读响应                  │
│  ═══════════════════════════════════════════     │
│              Network (Socket I/O via Okio)       │
└─────────────────────────────────────────────────┘
        │
        ▼
回调 Callback.onResponse / onFailure

九、核心设计模式总结

设计模式应用位置说明
责任链模式Interceptor Chain最核心设计,请求逐层传递
建造者模式OkHttpClient.Builder / Request.Builder复杂对象的构建
工厂模式Call.Factory (OkHttpClient实现)创建 Call 对象
策略模式CacheStrategy缓存策略的计算
享元模式ConnectionPool连接的复用与共享
观察者模式EventListener监听请求生命周期事件
外观模式OkHttpClient对外统一入口
不可变对象Request / Response / Headers线程安全

十、应用拦截器 vs 网络拦截器

                    Application          Network
                    Interceptor          Interceptor
                        │                    │
位置                 最外层               ConnectInterceptor之后
是否看到重定向响应     ×                    ✓ (每次网络调用都触发)
是否看到重试          ×                    ✓
缓存命中时是否执行     ✓                    × (短路,不走网络)
看到原始请求          ✓ (用户原始)          × (已被Bridge补全)
可修改最终请求头       ×                    ✓
典型用途             日志/统一参数/Token    流量监控/修改响应头
// 应用拦截器:无论缓存命中与否都执行
client.addInterceptor(chain -> {
    Request req = chain.request().newBuilder()
        .header("Token", getToken())
        .build();
    long t1 = System.nanoTime();
    Response response = chain.proceed(req);
    long t2 = System.nanoTime();
    Log.d("耗时: " + (t2 - t1) / 1e6 + "ms");
    return response;
});

// 网络拦截器:只在真正网络请求时执行
client.addNetworkInterceptor(chain -> {
    Response response = chain.proceed(chain.request());
    return response.newBuilder()
        .header("Cache-Control", "max-age=60")
        .build();
});

十一、EventListener — 全链路监控

public abstract class EventListener {
    callStart()
       │
    dnsStart() → dnsEnd()                    // DNS 解析
       │
    connectStart()                            // 连接开始
       ├── secureConnectStart() → secureConnectEnd()  // TLS
    connectEnd()                              // 连接结束
       │
    connectionAcquired()                      // 获得连接
       │
    requestHeadersStart() → requestHeadersEnd()
    requestBodyStart()    → requestBodyEnd()
       │
    responseHeadersStart() → responseHeadersEnd()
    responseBodyStart()    → responseBodyEnd()
       │
    connectionReleased()                      // 释放连接
       │
    callEnd() / callFailed()
}

十二、关键源码类导航

okhttp3/
├── OkHttpClient.kt          ← 配置中心 + Call.Factory
├── Request.kt                ← 请求模型
├── Response.kt               ← 响应模型
├── Call.kt                   ← 接口
├── RealCall.kt               ← Call 实现,构建拦截器链
├── Dispatcher.kt             ← 调度器
├── Cache.kt                  ← 缓存(基于DiskLruCache)
│
├── internal/connection/
│   ├── RealConnectionPool.kt ← 连接池实现
│   ├── RealConnection.kt     ← TCP/TLS 连接
│   ├── Exchange.kt           ← 单次请求-响应交换
│   └── ExchangeFinder.kt     ← 查找/创建可用连接
│
├── internal/http/
│   ├── RetryAndFollowUpInterceptor.kt
│   ├── BridgeInterceptor.kt
│   ├── CacheInterceptor.kt
│   ├── ConnectInterceptor.kt
│   ├── CallServerInterceptor.kt
│   └── RealInterceptorChain.kt  ← 责任链实现
│
└── internal/http2/
    ├── Http2Connection.kt     ← HTTP/2 连接管理
    └── Http2Stream.kt         ← HTTP/2

总结

OkHttp 架构设计的三大核心思想

  1. 拦截器责任链 — 将复杂的 HTTP 处理拆解为独立、可组合、可扩展的拦截器,每个拦截器只关注单一职责,用户可以在链条的不同位置插入自定义逻辑

  2. 连接复用 — 通过 ConnectionPool 实现 TCP/TLS 连接的缓存与复用,结合 HTTP/2 多路复用,极大降低了网络延迟和资源消耗

  3. 分层解耦 — 应用层(Request/Response)→ 调度层(Dispatcher)→ 协议层(Interceptors)→ 连接层(Connection)→ I/O 层(Okio),每层职责清晰,互不依赖