1. Android 第三方框架 OkHttp源码深度解析一:高效网络框架的奥秘

107 阅读19分钟

摘要:

OkHttp源码深度解析摘要

OkHttp核心基于责任链模式,通过五层拦截器链处理网络请求:

  1. 重试拦截器处理重定向和故障恢复
  2. 桥接拦截器添加头信息/处理Cookie
  3. 缓存拦截器管理本地缓存
  4. 连接拦截器建立TCP/HTTP2连接
  5. 服务调用拦截器执行网络I/O

通过Chain.proceed()实现双向递归调用:请求向下传递时预处理,响应返回时后处理。其连接池复用、HTTP/2支持和透明压缩机制保障了高性能,拦截器架构提供了灵活扩展性。

1. okhttp做了哪些优化

OkHttp 通过以下设计显著提升网络性能:

  • 连接复用:TCP 连接池减少重复握手开销(核心优化)
  • 请求压缩:自动添加 gzip 头,压缩响应数据
  • HTTP/2 支持:多路复用、头部压缩、服务端推送
  • 响应缓存:遵循 HTTP 缓存规范,减少重复请求
  • 无缝协议切换:支持 HTTP/1.1 → HTTP/2 自动升级
  • 智能重试:对非幂等请求安全重试

2.okhttp使用3步骤

import okhttp3.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class OkHttpExample {

    public static void main(String[] args) {
        // 步骤1:创建 OkHttpClient 实例(建议全局单例)
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .build();

        // 步骤2:构建 Request 对象
        Request request = new Request.Builder()
                .url("https://api.example.com/data")
                .header("Authorization", "Bearer token")
                .build();

        // 步骤3:发起同步请求(需在子线程执行)
        new Thread(() -> {
            try (Response response = client.newCall(request).execute()) {
                if (response.isSuccessful() && response.body() != null) {
                    System.out.println(response.body().string());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        // 异步请求(回调在子线程)
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.body() != null) {
                    System.out.println(response.body().string());
                }
            }

            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }
        });
    }
}

第一步: 创建OkHttpClient

第二步: 创建Request

第三步: OkHttpClient处理Request

3.okhttp整体框架

整体框架图

整体222.png

3条主线分析okhttp, 封装请求,分发和处理请求,处理响应

3.1 封装请求

架构图:

封装请求.png

Request request = new Request.Builder()
                .url("https://api.example.com/data")
                .header("Authorization", "Bearer token")
                .build();
里面源码具体的解读
// 源码位置:okhttp3/Request.java
public static class Builder {
    // 核心成员变量
    HttpUrl url;                 // 请求URL
    String method;               // HTTP方法 (GET/POST等)
    Headers.Builder headers;     // 请求头构建器
    RequestBody body;            // 请求体
    Object tag;                  // 请求标签(用于取消请求等)

    // 1. 构造方法
    public Builder() {
        this.method = "GET";      // 默认GET方法
        this.headers = new Headers.Builder();
    }

    // 2. URL设置方法(核心)
    public Builder url(HttpUrl url) {
        if (url == null) throw new NullPointerException("url == null");
        this.url = url;
        return this;
    }

    public Builder url(String url) {
        if (url == null) throw new NullPointerException("url == null");
        
        // 智能协议处理
        if (url.regionMatches(true, 0, "ws:", 0, 3)) {
            url = "http:" + url.substring(3);
        } else if (url.regionMatches(true, 0, "wss:", 0, 4)) {
            url = "https:" + url.substring(4);
        }
        
        // 解析URL
        HttpUrl parsed = HttpUrl.parse(url);
        if (parsed == null) throw new IllegalArgumentException("unexpected url: " + url);
        return url(parsed);
    }

    // 3. 请求头管理
    public Builder header(String name, String value) {
        headers.set(name, value);  // 替换同名header
        return this;
    }

    public Builder addHeader(String name, String value) {
        headers.add(name, value);  // 添加新header(不替换)
        return this;
    }

    public Builder removeHeader(String name) {
        headers.removeAll(name);
        return this;
    }

    // 4. 请求方法设置
    public Builder method(String method, RequestBody body) {
        // 方法名校验
        if (method == null) throw new NullPointerException("method == null");
        if (method.length() == 0) throw new IllegalArgumentException("method.length() == 0");
        
        // GET/HEAD方法不允许有请求体
        if (body != null && !HttpMethod.permitsRequestBody(method)) {
            throw new IllegalArgumentException("method " + method + " must not have a request body.");
        }
        
        // POST/PUT等方法必须有请求体
        if (body == null && HttpMethod.requiresRequestBody(method)) {
            throw new IllegalArgumentException("method " + method + " must have a request body.");
        }
        
        this.method = method;
        this.body = body;
        return this;
    }

    // 5. 快捷方法(内部调用method())
    public Builder get() {
        return method("GET", null);
    }

    public Builder post(RequestBody body) {
        return method("POST", body);
    }

    // 6. 构建最终Request对象
    public Request build() {
        if (url == null) throw new IllegalStateException("url == null");
        return new Request(this);
    }
}

通过建造者模式实现请求的封装

3.2 分发请求

架构图:

分发请求.png

okhttp的线程池逻辑

3.2.1. 请求入队入口
// RealCall.java
public void enqueue(Callback responseCallback) {
    synchronized (this) {
        if (executed) throw ...; // 防止重复执行
        executed = true;
    }
    // 核心:将回调封装为AsyncCall交给Dispatcher
    dispatcher.enqueue(new AsyncCall(responseCallback));
}
3.2.2 Dispatcher 调度逻辑(核心)
// Dispatcher.java
synchronized void enqueue(AsyncCall call) {
    // 检查是否满足立即执行条件:
    // 条件1:运行中请求数 < maxRequests(默认64)
    // 条件2:同一主机请求数 < maxRequestsPerHost(默认5)
    if (runningAsyncCalls.size() < maxRequests && 
        runningCallsForHost(call) < maxRequestsPerHost) {
        
        // 满足条件:加入运行队列并立即执行
        runningAsyncCalls.add(call);
        executorService().execute(call); // 提交线程池
        
    } else {
        // 不满足条件:加入等待队列
        readyAsyncCalls.add(call);
    }
}
3.2.3 线程池创建(关键配置)
public synchronized ExecutorService executorService() {
    if (executorService == null) {
        executorService = new ThreadPoolExecutor(
            0,                      // 核心线程数
            Integer.MAX_VALUE,      // 最大线程数
            60, TimeUnit.SECONDS,   // 空闲线程存活时间
            new SynchronousQueue<>(), // 任务队列
            Util.threadFactory("OkHttp Dispatcher", false)
        );
    }
    return executorService;
}

线程池特性分析

参数作用说明
corePoolSize0无核心线程,空闲时全部回收
maximumPoolSizeInteger.MAX_VALUE理论上可创建无限线程(实际受系统限制)
keepAliveTime60秒空闲线程存活时间
工作队列SynchronousQueue无容量队列,直接传递任务
3.2.4. 任务执行流程
// RealCall.AsyncCall.java
protected void execute() {
    try {
        // 执行拦截器链获取响应
        Response response = getResponseWithInterceptorChain();
        
        // 回调用户(在子线程执行!)
        responseCallback.onResponse(RealCall.this, response);
        
    } catch (IOException e) {
        responseCallback.onFailure(RealCall.this, e);
    } finally {
        // 关键:任务完成后触发后续调度
        dispatcher.finished(this);
    }
}
3.2.5. 任务完成后的队列调度
// Dispatcher.java
void finished(AsyncCall call) {
    // 1. 从运行队列移除
    synchronized (this) {
        if (!runningAsyncCalls.remove(call)) 
            throw new AssertionError("Call wasn't running!");
    }
    
    // 2. 检查等待队列并重新调度
    promoteAndExecute();
}

private void promoteAndExecute() {
    List<AsyncCall> executableCalls = new ArrayList<>();
    synchronized (this) {
        for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
            AsyncCall call = i.next();
            
            // 检查是否满足执行条件
            if (runningAsyncCalls.size() >= maxRequests) break;
            if (runningCallsForHost(call) >= maxRequestsPerHost) continue;
            
            i.remove();
            executableCalls.add(call);
            runningAsyncCalls.add(call);
        }
    }
    
    // 3. 执行符合条件的任务
    for (AsyncCall call : executableCalls) {
        executorService().execute(call);
    }
}
3.2.6 关键设计总结
  1. SynchronousQueue 的妙用

    • 零容量队列:每个插入操作必须等待对应的移除操作
    • 效果:当有可用线程时直接传递任务,无可用线程时立即创建新线程
    • 优势:避免任务排队,最大化并发性能
  2. 双队列协同机制

    • runningAsyncCalls:正在执行的请求(上限64)
    • readyAsyncCalls:等待执行的请求(无上限)
    • 智能调度:每次请求完成自动触发等待队列检查
  3. 主机级并发控制

    // 计算同一主机的运行中请求数
    private int runningCallsForHost(AsyncCall call) {
        int count = 0;
        for (AsyncCall c : runningAsyncCalls) {
            if (c.host().equals(call.host())) count++;
        }
        return count;
    }
    
    • 默认限制:同一主机最多5个并发请求
    • 防止对单个服务器造成DDoS效果
  4. 自动线程回收

    • 空闲线程60秒后自动销毁
    • 避免资源浪费:corePoolSize=0 确保完全回收

3.3 处理响应

架构图:

相应.png

okhttp的5大拦截器

拦截器.png

3.3.1. okhttp拦截器之重定向拦截器

图解:

retry.png

核心功能

  1. 网络故障重试:处理连接超时、路由失败等可恢复异常
  2. HTTP重定向:自动处理3xx状态码(最多20次)
  3. 认证挑战处理:响应401/407状态码自动重试
  4. 协议降级处理:HTTP/2连接失败时降级到HTTP/1.1
类结构定义
public final class RetryAndFollowUpInterceptor implements Interceptor {
    // 最大重定向次数
    private static final int MAX_FOLLOW_UPS = 20;
    
    // 线程安全的客户端配置
    private final OkHttpClient client;
    
    // 请求重定向跟踪
    private StreamAllocation streamAllocation;
}
核心拦截方法
@Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    
    // 1. 初始化资源分配器
    streamAllocation = new StreamAllocation(
        client.connectionPool(), 
        createAddress(request.url()),
        callStackTrace
    );

    int followUpCount = 0;
    Response priorResponse = null;
    
    while (true) {
        try {
            // 2. 执行后续拦截器
            Response response = chain.proceed(request);
            
            // 3. 检查是否需要跟进处理
            Request followUp = followUpRequest(response, priorResponse);
            
            // 4. 无需跟进则返回响应
            if (followUp == null) {
                return response;
            }
            
            // 5. 检查重定向次数限制
            if (++followUpCount > MAX_FOLLOW_UPS) {
                throw new ProtocolException("Too many follow-up requests: " + followUpCount);
            }
            
            // 6. 关闭当前响应体资源
            response.close();
            priorResponse = response;
            request = followUp;
            
        } catch (IOException e) {
            // 7. 判断异常是否可恢复
            if (!recover(e, request)) {
                throw e;
            }
            
            // 8. 释放资源准备重试
            streamAllocation.release();
            priorResponse = null;
            continue;
        }
    }
}

3.3.1.2 关键子方法解析
1. 跟进请求构建(followUpRequest)
private Request followUpRequest(Response response, Response priorResponse) throws IOException {
    // 0. 获取响应码
    int responseCode = response.code();
    
    switch (responseCode) {
        // 1. 认证挑战处理
        case HTTP_UNAUTHORIZED: // 401
            return handleAuthenticationChallenge(response, "WWW-Authenticate");
            
        case HTTP_PROXY_AUTH: // 407
            return handleAuthenticationChallenge(response, "Proxy-Authenticate");
            
        // 2. 重定向处理 (300, 301, 302, 303, 307, 308)
        case HTTP_MULT_CHOICE: 
        case HTTP_MOVED_PERM:
        case HTTP_MOVED_TEMP:
        case HTTP_SEE_OTHER:
            return buildRedirectRequest(response, responseCode);
            
        // 3. 特殊状态码处理
        case HTTP_PERM_REDIRECT: // 308
            if (!method.equals("GET") && !method.equals("HEAD")) {
                return null; // 308不允许修改方法
            }
            return buildRedirectRequest(response, responseCode);
            
        // 4. 客户端超时处理 (408)
        case HTTP_CLIENT_TIMEOUT: 
            if (response.header("Connection") != "close") {
                return request; // 直接重试原请求
            }
            break;
    }
    return null; // 无需跟进
}
2. 认证挑战处理(handleAuthenticationChallenge)
private Request handleAuthenticationChallenge(Response response, String headerName) {
    // 1. 获取认证头
    List<Challenge> challenges = response.challenges();
    
    // 2. 获取当前路由信息
    Route route = streamAllocation.route();
    
    // 3. 选择认证器(客户端/代理)
    Authenticator authenticator = (responseCode == HTTP_PROXY_AUTH) 
        ? client.proxyAuthenticator() 
        : client.authenticator();
    
    // 4. 构建认证请求
    return authenticator.authenticate(route, response);
}
3. 重定向请求构建(buildRedirectRequest)
private Request buildRedirectRequest(Response response, int responseCode) {
    // 1. 获取重定向地址
    String location = response.header("Location");
    HttpUrl url = response.request().url().resolve(location);
    
    // 2. 特殊方法处理
    String method = response.request().method();
    if (responseCode == HTTP_SEE_OTHER) {
        method = "GET"; // 303必须转GET
    }
    
    // 3. 请求体处理(重定向后不携带)
    RequestBody requestBody = null;
    if (!HttpMethod.permitsRequestBody(method)) {
        requestBody = null;
    }
    
    // 4. 构建新请求
    return response.request().newBuilder()
        .url(url)
        .method(method, requestBody)
        .build();
}
4. 异常恢复判断(recover)
private boolean recover(IOException e, Request request) {
    // 1. 不可恢复的协议异常
    if (e instanceof ProtocolException) return false;
    
    // 2. 中断异常不重试
    if (e instanceof InterruptedIOException) {
        return (e instanceof SocketTimeoutException); // 仅超时可重试
    }
    
    // 3. SSL握手失败不重试
    if (e instanceof SSLHandshakeException) return false;
    if (e instanceof SSLPeerUnverifiedException) return false;
    
    // 4. 检查路由是否可用
    if (!streamAllocation.hasMoreRoutes()) return false;
    
    // 5. 非幂等方法不重试
    if (!request.isRepeatable()) return false;
    
    // 6. 客户端配置允许重试
    return client.retryOnConnectionFailure();
}

3.3.2. okhttp拦截器之BridgeInterceptor

核心职责与功能

bridget1.png

核心功能

  1. 请求头补全:添加必要HTTP头(User-Agent、Host等)
  2. 请求体转换:处理Content-Type/Content-Length
  3. 响应体处理:自动解压Gzip响应
  4. Cookie管理:自动处理请求/响应Cookie
  5. 连接头管理:添加Keep-Alive/Connection头

类结构定义
public final class BridgeInterceptor implements Interceptor {
    // Cookie管理器
    private final CookieJar cookieJar;
    
    public BridgeInterceptor(CookieJar cookieJar) {
        this.cookieJar = cookieJar;
    }
}
核心拦截方法
@Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    
    // 1. 构建请求构造器
    Request.Builder requestBuilder = userRequest.newBuilder();
    
    // 2. 补全请求头 =================================
    // 2.1 处理请求体头信息
    RequestBody body = userRequest.body();
    if (body != null) {
        // Content-Type
        MediaType contentType = body.contentType();
        if (contentType != null) {
            requestBuilder.header("Content-Type", contentType.toString());
        }
        
        // Content-Length
        long contentLength = body.contentLength();
        if (contentLength != -1) {
            requestBuilder.header("Content-Length", Long.toString(contentLength));
            requestBuilder.removeHeader("Transfer-Encoding"); // 移除分块传输
        } else {
            requestBuilder.header("Transfer-Encoding", "chunked");
            requestBuilder.removeHeader("Content-Length");
        }
    }
    
    // 2.2 添加通用头
    if (userRequest.header("Host") == null) {
        requestBuilder.header("Host", hostHeader(userRequest.url(), false));
    }
    if (userRequest.header("Connection") == null) {
        requestBuilder.header("Connection", "Keep-Alive"); // 保持连接
    }
    
    // 2.3 压缩支持
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
        transparentGzip = true;
        requestBuilder.header("Accept-Encoding", "gzip"); // 声明支持Gzip
    }
    
    // 2.4 用户代理
    if (userRequest.header("User-Agent") == null) {
        requestBuilder.header("User-Agent", Version.userAgent());
    }
    
    // 3. 处理Cookie ================================
    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
        requestBuilder.header("Cookie", cookieHeader(cookies));
    }
    
    // 4. 执行网络请求 ===============================
    Response networkResponse = chain.proceed(requestBuilder.build());
    
    // 5. 处理响应Cookie =============================
    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
    
    // 6. 处理响应体 ================================
    Response.Builder responseBuilder = networkResponse.newBuilder()
        .request(userRequest);
    
    // 7. Gzip解压处理
    if (transparentGzip 
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
        
        // 创建解压流
        GzipSource responseBody = new GzipSource(networkResponse.body().source());
        
        // 重建响应头(移除压缩相关头)
        Headers strippedHeaders = networkResponse.headers().newBuilder()
            .removeAll("Content-Encoding")
            .removeAll("Content-Length")
            .build();
        
        // 构建新响应
        responseBuilder.headers(strippedHeaders);
        responseBuilder.body(new RealResponseBody(
            strippedHeaders, 
            Okio.buffer(responseBody)
        ));
    }
    
    return responseBuilder.build();
}

关键子方法解析
1. Host头处理(hostHeader)
private String hostHeader(HttpUrl url, boolean includeDefaultPort) {
    String host = url.host().contains(":") 
        ? "[" + url.host() + "]"  // IPv6地址处理
        : url.host();
    
    return includeDefaultPort || url.port() != HttpUrl.defaultPort(url.scheme())
        ? host + ":" + url.port()
        : host;
}
2. Cookie头构建(cookieHeader)
private String cookieHeader(List<Cookie> cookies) {
    StringBuilder cookieHeader = new StringBuilder();
    for (int i = 0, size = cookies.size(); i < size; i++) {
        if (i > 0) {
            cookieHeader.append("; ");
        }
        Cookie cookie = cookies.get(i);
        cookieHeader.append(cookie.name()).append('=').append(cookie.value());
    }
    return cookieHeader.toString();
}
3. Gzip解压处理
// 创建Gzip解压流
GzipSource responseBody = new GzipSource(networkResponse.body().source());

// 重建响应体
ResponseBody responseBody = new RealResponseBody(
    strippedHeaders, 
    Okio.buffer(responseBody) // 包装解压流
);
1. 请求头补全机制
补充的头字段作用补全逻辑
Content-Type声明请求体格式从RequestBody中提取
Content-Length声明请求体大小根据body.contentLength()计算
Transfer-Encoding分块传输标识当contentLength=-1时设为chunked
Host目标主机标识自动从URL提取,处理IPv6格式
Connection连接控制固定Keep-Alive保持连接复用
Accept-Encoding压缩支持自动添加gzip(当用户未指定时)
User-Agent客户端标识使用OkHttp版本号:okhttp/4.12.0

总结

bridge.png

BridgeInterceptor 是OkHttp的"协议适配层",主要实现三大转换:

  1. 用户请求 → 网络请求

    • 补全协议必要头信息
    • 自动处理Cookie
    • 智能压缩请求
  2. 网络响应 → 用户响应

    • 自动解压Gzip响应
    • 清理协议相关头
    • 保留业务相关头
  3. 协议细节隐藏

    • 透明处理分块传输
    • 自动管理连接状态
    • 统一IPv4/IPv6处理

3.3.3. okhttp的缓存机制 (重点)

核心功能

cache.png

  1. 缓存查找:根据请求查找匹配的缓存响应
  2. 缓存验证:通过条件请求验证缓存有效性
  3. 缓存更新:处理304响应更新缓存
  4. 响应存储:缓存符合条件的网络响应
  5. 缓存策略:实现HTTP缓存规范(RFC 7234)

类结构定义
public final class CacheInterceptor implements Interceptor {
    // 缓存实现(内部使用DiskLruCache)
    final InternalCache cache;
    
    public CacheInterceptor(InternalCache cache) {
        this.cache = cache;
    }
}
核心拦截方法
@Override public Response intercept(Chain chain) throws IOException {
    // 1. 尝试获取缓存
    Response cacheCandidate = cache != null
        ? cache.get(chain.request())
        : null;
    
    // 2. 创建缓存策略
    CacheStrategy strategy = new CacheStrategy.Factory(
        now, chain.request(), cacheCandidate
    ).get();
    
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
    
    // 3. 记录缓存使用情况
    if (cache != null) {
        cache.trackResponse(strategy);
    }
    
    // 4. 无缓存且禁止网络 → 504错误
    if (cacheCandidate != null && cacheResponse == null) {
        cacheCandidate.body().close(); // 关闭未使用缓存
    }
    
    // 5. 直接使用缓存
    if (networkRequest == null && cacheResponse != null) {
        return cacheResponse.newBuilder()
            .cacheResponse(stripBody(cacheResponse))
            .build();
    }
    
    // 6. 发起网络请求
    Response networkResponse = null;
    try {
        networkResponse = chain.proceed(networkRequest);
    } finally {
        // 7. 网络异常时关闭缓存
        if (networkResponse == null && cacheCandidate != null) {
            cacheCandidate.body().close();
        }
    }
    
    // 8. 处理304响应(缓存更新)
    if (cacheResponse != null) {
        if (networkResponse.code() == HTTP_NOT_MODIFIED) {
            Response response = cacheResponse.newBuilder()
                .headers(combine(cacheResponse.headers(), networkResponse.headers()))
                .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
                .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
                .cacheResponse(stripBody(cacheResponse))
                .networkResponse(stripBody(networkResponse))
                .build();
            // 更新缓存
            cache.trackConditionalCacheHit();
            cache.update(cacheResponse, response);
            return response;
        } else {
            cacheCandidate.body().close();
        }
    }
    
    // 9. 构建最终响应
    Response response = networkResponse.newBuilder()
        .cacheResponse(stripBody(cacheResponse))
        .networkResponse(stripBody(networkResponse))
        .build();
    
    // 10. 缓存新响应
    if (cache != null) {
        if (HttpHeaders.hasBody(response) 
            && CacheStrategy.isCacheable(response, networkRequest)) {
            CacheRequest cacheRequest = cache.put(response);
            return cacheWritingResponse(cacheRequest, response);
        }
        
        // 处理不可缓存的方法(如POST)
        if (HttpMethod.invalidatesCache(networkRequest.method())) {
            try {
                cache.remove(networkRequest);
            } catch (IOException ignored) {
            }
        }
    }
    
    return response;
}

3.3.3.2 核心子方法解析
1. 缓存策略决策(CacheStrategy.Factory)
public Factory(long nowMillis, Request request, Response cacheResponse) {
    this.nowMillis = nowMillis;
    this.request = request;
    this.cacheResponse = cacheResponse;
    
    if (cacheResponse != null) {
        // 解析缓存头
        this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
        this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
        Headers headers = cacheResponse.headers();
        for (int i = 0, size = headers.size(); i < size; i++) {
            String fieldName = headers.name(i);
            String value = headers.value(i);
            switch (fieldName) {
                case "Date": date = HttpDate.parse(value); break;
                case "Expires": expires = HttpDate.parse(value); break;
                case "Last-Modified": lastModified = HttpDate.parse(value); break;
                case "ETag": etag = value; break;
                case "Age": ageSeconds = HttpHeaders.parseSeconds(value, -1); break;
            }
        }
    }
}

public CacheStrategy get() {
    // 1. 检查缓存有效性
    if (cacheResponse == null) {
        return new CacheStrategy(request, null); // 无缓存 → 网络请求
    }
    
    // 2. 检查请求方法(GET/HEAD才可缓存)
    if (!"GET".equals(request.method())) {
        return new CacheStrategy(request, null); // 非GET → 网络请求
    }
    
    // 3. 检查缓存控制头(no-cache/no-store)
    if (request.cacheControl().noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null); // 强制刷新 → 网络请求
    }
    
    // 4. 计算缓存新鲜度
    long ageMillis = cacheResponseAge();
    long freshMillis = computeFreshnessLifetime();
    
    if (request.cacheControl().maxAgeSeconds() != -1) {
        freshMillis = Math.min(freshMillis, 
            TimeUnit.SECONDS.toMillis(request.cacheControl().maxAgeSeconds()));
    }
    
    long minFreshMillis = 0;
    if (request.cacheControl().minFreshSeconds() != -1) {
        minFreshMillis = TimeUnit.SECONDS.toMillis(request.cacheControl().minFreshSeconds());
    }
    
    long maxStaleMillis = 0;
    if (!request.cacheControl().onlyIfCached() && request.cacheControl().maxStaleSeconds() != -1) {
        maxStaleMillis = TimeUnit.SECONDS.toMillis(request.cacheControl().maxStaleSeconds());
    }
    
    // 5. 决策逻辑
    if (!request.cacheControl().noCache() 
        && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        
        // 缓存新鲜 → 直接使用
        return new CacheStrategy(null, cacheResponse);
    }
    
    // 6. 构建条件请求
    Request.Builder conditionalRequestBuilder = request.newBuilder();
    if (etag != null) {
        conditionalRequestBuilder.header("If-None-Match", etag);
    } else if (lastModified != null) {
        conditionalRequestBuilder.header("If-Modified-Since", lastModifiedString);
    }
    return new CacheStrategy(conditionalRequestBuilder.build(), cacheResponse);
}
2. 缓存写入(cacheWritingResponse)
private Response cacheWritingResponse(
    final CacheRequest cacheRequest, Response response) throws IOException {
    
    // 创建缓存写入流
    Sink cacheBodyUnbuffered = cacheRequest.body();
    if (cacheBodyUnbuffered == null) return response;
    
    // 双写流:同时写入缓存和返回给用户
    final BufferedSource source = response.body().source();
    final BufferedSink cacheBody = Okio.buffer(cacheBodyUnbuffered);
    
    Source cacheWritingSource = new Source() {
        boolean cacheRequestClosed;
        
        @Override public long read(Buffer sink, long byteCount) throws IOException {
            long bytesRead = source.read(sink, byteCount);
            if (bytesRead == -1) {
                closeCacheRequest(); // 结束写入
                return -1;
            }
            
            // 写入缓存
            sink.copyTo(cacheBody.buffer(), sink.size() - bytesRead, bytesRead);
            cacheBody.emitCompleteSegments();
            return bytesRead;
        }
        
        private void closeCacheRequest() {
            if (!cacheRequestClosed) {
                cacheRequestClosed = true;
                cacheBody.close();
            }
        }
    };
    
    // 构建新响应体
    return response.newBuilder()
        .body(new RealResponseBody(
            response.headers(),
            Okio.buffer(cacheWritingSource)
        ))
        .build();
}

缓存策略矩阵

条件处理方式HTTP行为
无缓存发起网络请求正常请求
缓存新鲜直接返回缓存不产生网络请求
缓存过期但可验证发送条件请求If-Modified-Since/If-None-Match
收到304更新缓存后返回使用缓存内容
收到200替换旧缓存存储新响应
POST请求跳过缓存直接网络请求

缓存控制头处理逻辑

请求头作用示例
Cache-Control: max-age=3600指定缓存新鲜时间优先使用
Cache-Control: no-cache强制验证缓存发送条件请求
Cache-Control: no-store禁止存储跳过缓存逻辑
Cache-Control: only-if-cached仅用缓存无网络请求
响应头作用示例
Cache-Control: public允许公共缓存可被共享缓存
Cache-Control: private私有缓存仅限用户缓存
ETag: "abc"实体标签用于条件请求
Expires: Wed, 21 Oct 2025 07:28:00 GMT绝对过期时间备用方案

缓存2.png

3.3.4. okhttp拦截器之ConnectInterceptor

connect.png

链接池复用(重点)

源码分析(OkHttp 4.12.0)
类结构定义
public final class ConnectInterceptor implements Interceptor {
    // 客户端配置
    private final OkHttpClient client;
    
    public ConnectInterceptor(OkHttpClient client) {
        this.client = client;
    }
}
核心拦截方法
@Override public Response intercept(Chain chain) throws IOException {
    // 1. 获取当前请求和流分配器
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    
    // 2. 检查是否需要发送请求体
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    
    // 3. 关键:建立连接并创建数据流
    HttpCodec httpCodec = streamAllocation.newStream(
        client, chain, doExtensiveHealthChecks);
    
    // 4. 获取实际连接对象
    RealConnection connection = streamAllocation.connection();
    
    // 5. 继续执行后续拦截器
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
}

3.3.4.2 核心组件深度解析
3.3.4.2.1. StreamAllocation(流分配器)
// 创建新数据流
public HttpCodec newStream(OkHttpClient client, Interceptor.Chain chain, 
                          boolean doExtensiveHealthChecks) {
    
    // 1. 寻找可用连接
    RealConnection resultConnection = findConnection(
        connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, doExtensiveHealthChecks);
    
    // 2. 创建协议适配器
    HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
    
    // 3. 关联流与连接
    synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
    }
}
3.3.4.2.2. 连接查找流程(findConnection)
private RealConnection findConnection(...) throws IOException {
    // 尝试1:检查当前分配连接
    if (connection != null) {
        return connection;
    }
    
    // 尝试2:从连接池获取
    if (connectionPool.callAcquirePooledConnection(address, this, null, false)) {
        return connection;
    }
    
    // 尝试3:创建新连接
    route = nextRoute();
    newConnection = new RealConnection(connectionPool, route);
    
    // 建立实际连接
    newConnection.connect(
        connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,
        connectionRetryEnabled, call, eventListener);
    
    // 添加到连接池
    connectionPool.put(newConnection);
    return newConnection;
}
3.3.4.2.3. 实际连接建立(RealConnection.connect)
public void connect(...) throws IOException {
    // 1. 建立TCP连接
    while (true) {
        try {
            // 直连或代理连接
            if (route.requiresTunnel()) {
                connectTunnel(...);
            } else {
                connectSocket(...);
            }
            break;
        } catch (IOException e) {
            // 处理重试逻辑
        }
    }
    
    // 2. 建立TLS连接(HTTPS)
    if (route.address().sslSocketFactory() != null) {
        establishSecureConnection(...);
    }
    
    // 3. 协议协商
    if (http2Connection != null) {
        startHttp2(...);
    }
}
五大核心功能详解
1. 连接复用机制

复用原理.png 复用条件

  • 相同主机(host + port)
  • 相同代理配置
  • 相同TLS配置(证书、协议版本等)
  • 连接状态健康(未关闭)
2. 路由选择策略
// RouteSelector.java
public List<Route> getAll() {
    // 1. 获取代理列表
    proxies = address.proxySelector().select(url);
    
    // 2. DNS解析获取IP地址
    inetSocketAddresses = address.dns().lookup(host);
    
    // 3. 组合路由
    for (Proxy proxy : proxies) {
        for (InetAddress inetAddress : inetSocketAddresses) {
            routes.add(new Route(address, proxy, inetAddress));
        }
    }
}
  • 路由顺序:直连 → SOCKS代理 → HTTP代理
  • 失败重试:自动尝试下一个路由
3. TLS握手流程
private void establishSecureConnection(...) throws IOException {
    // 1. 创建SSLSocket
    sslSocket = (SSLSocket) sslSocketFactory.createSocket(...);
    
    // 2. 配置TLS参数
    ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
    
    // 3. 开始握手
    sslSocket.startHandshake();
    
    // 4. 证书验证
    Handshake handshake = Handshake.get(sslSocket.getSession());
    address.certificatePinner().check(host, handshake.peerCertificates());
    
    // 5. 保存会话信息
    this.handshake = handshake;
}
4. HTTP/2协议协商
private void startHttp2(int pingIntervalMillis) throws IOException {
    // 1. 发送Preface帧
    writer.connectionPreface();
    
    // 2. 发送SETTINGS帧
    writer.settings(okHttpSettings);
    
    // 3. 创建HTTP/2连接对象
    http2Connection = new Http2Connection.Builder(true)
        .socket(socket, route.address().url().host(), source, sink)
        .build();
    
    // 4. 启动读线程
    http2Connection.start();
}
5. 连接健康检查
public boolean isHealthy(boolean doExtensiveChecks) {
    // 1. 检查socket是否关闭
    if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
        return false;
    }
    
    // 2. HTTP/2连接检查
    if (http2Connection != null) {
        return !http2Connection.isShutdown();
    }
    
    // 3. 深度检查:尝试读取1字节
    if (doExtensiveChecks) {
        try {
            int readTimeout = socket.getSoTimeout();
            socket.setSoTimeout(1);
            if (source.exhausted()) {
                return false;
            }
            socket.setSoTimeout(readTimeout);
        } catch (SocketTimeoutException ignored) {
            // 读取超时表示连接健康
        } catch (IOException e) {
            return false; // 发生错误
        }
    }
    
    return true;
}

性能优化技术

技术实现方式效果
连接复用相同主机复用TCP连接减少TCP握手开销
连接池维护活跃连接队列避免频繁创建销毁
HTTP/2多路复用单连接并行多个请求减少连接数量
TLS会话复用SessionTicket扩展减少TLS握手开销
TCP快速打开TFO选项设置加速TCP建立
协议优先选择ALPN协商自动选择最优协议

3.3.5. okhttp拦截器之CallServerInterceptor

核心功能

  1. 请求发送:将请求头和请求体写入网络
  2. 响应接收:读取响应头和响应体
  3. 协议处理:处理HTTP/1.1和HTTP/2差异
  4. 流控制:管理数据流传输
  5. 网络I/O:实际Socket读写操作

类结构定义
public final class CallServerInterceptor implements Interceptor {
    // 是否支持长连接(Keep-Alive)
    private final boolean forWebSocket;
    
    public CallServerInterceptor(boolean forWebSocket) {
        this.forWebSocket = forWebSocket;
    }
}
核心拦截方法
@Override public Response intercept(Chain chain) throws IOException {
    // 1. 获取网络组件
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    HttpCodec httpCodec = realChain.httpStream();
    StreamAllocation streamAllocation = realChain.streamAllocation();
    RealConnection connection = (RealConnection) realChain.connection();
    Request request = realChain.request();
    
    // 2. 记录请求时间
    long sentRequestMillis = System.currentTimeMillis();
    
    // 3. 发送请求头
    httpCodec.writeRequestHeaders(request);
    
    // 4. 处理请求体(如果有)
    Response.Builder responseBuilder = null;
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
        // 4.1 处理Expect: 100-continue
        if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
            httpCodec.flushRequest();
            responseBuilder = httpCodec.readResponseHeaders(true);
        }
        
        // 4.2 实际发送请求体
        if (responseBuilder == null) {
            Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
            BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
            request.body().writeTo(bufferedRequestBody);
            bufferedRequestBody.close();
        }
    }
    
    // 5. 结束请求(HTTP/1.1需要)
    httpCodec.finishRequest();
    
    // 6. 读取响应头
    if (responseBuilder == null) {
        responseBuilder = httpCodec.readResponseHeaders(false);
    }
    
    // 7. 构建响应对象
    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
    
    // 8. 处理特殊响应码
    int code = response.code();
    if (code == 100) { // 100 Continue
        // 继续读取实际响应
        responseBuilder = httpCodec.readResponseHeaders(false);
        response = responseBuilder
            .request(request)
            .handshake(streamAllocation.connection().handshake())
            .sentRequestAtMillis(sentRequestMillis)
            .receivedResponseAtMillis(System.currentTimeMillis())
            .build();
        code = response.code();
    }
    
    // 9. 读取响应体
    response = response.newBuilder()
        .body(httpCodec.openResponseBody(response))
        .build();
    
    // 10. 处理连接关闭
    if ("close".equalsIgnoreCase(response.request().header("Connection"))
        || "close".equalsIgnoreCase(response.header("Connection"))) {
        streamAllocation.noNewStreams(); // 标记连接不可复用
    }
    
    // 11. 返回最终响应
    return response;
}
HTTP/1.1 vs HTTP/2实现差异
功能HTTP/1.1HTTP/2
请求头发送文本格式HPACK压缩
多路复用不支持单连接多流
优先级流优先级控制
头块分割不支持HEADERS/CONTINUATION帧
数据流控制WINDOW_UPDATE帧
服务器推送不支持PUSH_PROMISE帧

4. OkHttp框架中用了哪些数据结构

数据结构应用场景优势
双端队列存储异步请求(Dispatcher)高效任务调度
LRU 缓存连接池复用(ConnectionPool)自动淘汰陈旧连接
DiskLruCache响应缓存存储文件系统级缓存管理
SynchronousQueue线程池任务传递零容量直接传递任务
路由表管理代理和IP地址(Route)支持复杂网络环境

总结

OkHttp 通过 连接池复用 + 拦截器责任链 + 智能缓存 三大支柱实现高性能网络通信。其设计精髓在于:

  1. 资源复用最大化(TCP连接/线程)
  2. 扩展性极强的拦截器体系
  3. 严格遵循HTTP协议规范
  4. 高效的内存/磁盘管理策略

最完整的架构图说明

1. 用户层(紫色)
  • OkHttpClient:全局配置中心(连接池、超时设置等)
  • Request:请求封装(URL、方法、头、体)
  • Call:请求执行接口(同步/异步)
2. 核心处理层(蓝色)
  • Dispatcher:请求分发器

    • 管理同步请求队列(RunningSyncCalls)
    • 管理异步请求队列(AsyncCallQueue)
    • 控制最大请求数(默认64)

队列22.png

  • 线程池:动态线程管理

    • 核心线程数:0
    • 最大线程数:Integer.MAX_VALUE
    • 空闲线程存活时间:60秒

并发原理.png

  • 拦截器链

    • RetryAndFollowUp:重试/重定向(最多20次)
    • Bridge:添加通用头/Cookie管理
    • Cache:磁盘缓存处理(DiskLruCache)
    • Connect:连接管理(连接池/路由/TLS)
    • CallServer:网络I/O操作
3. 基础设施层(橙色)
  • 连接池(ConnectionPool):

    • 默认最大空闲连接数:5
    • 空闲连接存活时间:5分钟
  • 路由选择(RouteSelector):

    • 代理支持:DIRECT/HTTP/SOCKS
    • 自动路由故障转移
  • 协议支持

    • HTTP/1.1 管道化
    • HTTP/2 多路复用
    • WebSocket 全双工通信
  • 安全通信

    • TLS 1.2/1.3
    • 证书链验证
    • SNI支持
4. 网络层(绿色)
  • Okio:高效I/O库

    • 分块缓冲管理
    • 零拷贝文件传输
  • Socket:底层网络通信

    • TCP连接管理
    • 超时控制
5. 支持系统(青色)
  • 事件监听(EventListener):

    • 请求生命周期监控
    • 性能指标采集
  • 连接监控(ConnectionSpec):

    • 加密配置(TLS版本/密码套件)
  • 证书锁定(CertificatePinner):

    • 防止中间人攻击
  • 代理支持(Proxy):

    • 自动系统代理检测
  • 超时控制(Timeout):

    • 连接/读取/写入超时

数据流向

完整的数据流向.png

核心设计特点

  1. 分层架构

    • 用户层:简单API
    • 核心层:拦截器责任链
    • 基础设施:可插拔组件
    • 网络层:协议无关I/O
  2. 智能连接管理

多协议支持矩阵

协议特点适用场景
HTTP/1.1简单兼容通用请求
HTTP/2多路复用高并发请求
WebSocket全双工实时通信
HTTPS加密传输安全敏感数据

高并发请求处理