摘要:
OkHttp源码深度解析摘要
OkHttp核心基于责任链模式,通过五层拦截器链处理网络请求:
- 重试拦截器处理重定向和故障恢复
- 桥接拦截器添加头信息/处理Cookie
- 缓存拦截器管理本地缓存
- 连接拦截器建立TCP/HTTP2连接
- 服务调用拦截器执行网络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整体框架
整体框架图
3条主线分析okhttp, 封装请求,分发和处理请求,处理响应
3.1 封装请求
架构图:
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 分发请求
架构图:
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;
}
线程池特性分析:
| 参数 | 值 | 作用说明 |
|---|---|---|
| corePoolSize | 0 | 无核心线程,空闲时全部回收 |
| maximumPoolSize | Integer.MAX_VALUE | 理论上可创建无限线程(实际受系统限制) |
| keepAliveTime | 60秒 | 空闲线程存活时间 |
| 工作队列 | 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 关键设计总结
-
SynchronousQueue 的妙用
- 零容量队列:每个插入操作必须等待对应的移除操作
- 效果:当有可用线程时直接传递任务,无可用线程时立即创建新线程
- 优势:避免任务排队,最大化并发性能
-
双队列协同机制
runningAsyncCalls:正在执行的请求(上限64)readyAsyncCalls:等待执行的请求(无上限)- 智能调度:每次请求完成自动触发等待队列检查
-
主机级并发控制
// 计算同一主机的运行中请求数 private int runningCallsForHost(AsyncCall call) { int count = 0; for (AsyncCall c : runningAsyncCalls) { if (c.host().equals(call.host())) count++; } return count; }- 默认限制:同一主机最多5个并发请求
- 防止对单个服务器造成DDoS效果
-
自动线程回收
- 空闲线程60秒后自动销毁
- 避免资源浪费:
corePoolSize=0确保完全回收
3.3 处理响应
架构图:
okhttp的5大拦截器
3.3.1. okhttp拦截器之重定向拦截器
图解:
核心功能:
- 网络故障重试:处理连接超时、路由失败等可恢复异常
- HTTP重定向:自动处理3xx状态码(最多20次)
- 认证挑战处理:响应401/407状态码自动重试
- 协议降级处理: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
核心职责与功能
核心功能:
- 请求头补全:添加必要HTTP头(User-Agent、Host等)
- 请求体转换:处理Content-Type/Content-Length
- 响应体处理:自动解压Gzip响应
- Cookie管理:自动处理请求/响应Cookie
- 连接头管理:添加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 |
总结
BridgeInterceptor 是OkHttp的"协议适配层",主要实现三大转换:
-
用户请求 → 网络请求
- 补全协议必要头信息
- 自动处理Cookie
- 智能压缩请求
-
网络响应 → 用户响应
- 自动解压Gzip响应
- 清理协议相关头
- 保留业务相关头
-
协议细节隐藏
- 透明处理分块传输
- 自动管理连接状态
- 统一IPv4/IPv6处理
3.3.3. okhttp的缓存机制 (重点)
核心功能:
- 缓存查找:根据请求查找匹配的缓存响应
- 缓存验证:通过条件请求验证缓存有效性
- 缓存更新:处理304响应更新缓存
- 响应存储:缓存符合条件的网络响应
- 缓存策略:实现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 | 绝对过期时间 | 备用方案 |
3.3.4. okhttp拦截器之ConnectInterceptor
链接池复用(重点)
源码分析(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. 连接复用机制
复用条件:
- 相同主机(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
核心功能:
- 请求发送:将请求头和请求体写入网络
- 响应接收:读取响应头和响应体
- 协议处理:处理HTTP/1.1和HTTP/2差异
- 流控制:管理数据流传输
- 网络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.1 | HTTP/2 |
|---|---|---|
| 请求头发送 | 文本格式 | HPACK压缩 |
| 多路复用 | 不支持 | 单连接多流 |
| 优先级 | 无 | 流优先级控制 |
| 头块分割 | 不支持 | HEADERS/CONTINUATION帧 |
| 数据流控制 | 无 | WINDOW_UPDATE帧 |
| 服务器推送 | 不支持 | PUSH_PROMISE帧 |
4. OkHttp框架中用了哪些数据结构
| 数据结构 | 应用场景 | 优势 |
|---|---|---|
| 双端队列 | 存储异步请求(Dispatcher) | 高效任务调度 |
| LRU 缓存 | 连接池复用(ConnectionPool) | 自动淘汰陈旧连接 |
| DiskLruCache | 响应缓存存储 | 文件系统级缓存管理 |
| SynchronousQueue | 线程池任务传递 | 零容量直接传递任务 |
| 路由表 | 管理代理和IP地址(Route) | 支持复杂网络环境 |
总结
OkHttp 通过 连接池复用 + 拦截器责任链 + 智能缓存 三大支柱实现高性能网络通信。其设计精髓在于:
- 资源复用最大化(TCP连接/线程)
- 扩展性极强的拦截器体系
- 严格遵循HTTP协议规范
- 高效的内存/磁盘管理策略
最完整的架构图说明
1. 用户层(紫色)
- OkHttpClient:全局配置中心(连接池、超时设置等)
- Request:请求封装(URL、方法、头、体)
- Call:请求执行接口(同步/异步)
2. 核心处理层(蓝色)
-
Dispatcher:请求分发器
- 管理同步请求队列(RunningSyncCalls)
- 管理异步请求队列(AsyncCallQueue)
- 控制最大请求数(默认64)
-
线程池:动态线程管理
- 核心线程数:0
- 最大线程数:Integer.MAX_VALUE
- 空闲线程存活时间:60秒
-
拦截器链:
- 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):
- 连接/读取/写入超时
数据流向
核心设计特点
-
分层架构:
- 用户层:简单API
- 核心层:拦截器责任链
- 基础设施:可插拔组件
- 网络层:协议无关I/O
-
智能连接管理:
多协议支持矩阵:
| 协议 | 特点 | 适用场景 |
|---|---|---|
| HTTP/1.1 | 简单兼容 | 通用请求 |
| HTTP/2 | 多路复用 | 高并发请求 |
| WebSocket | 全双工 | 实时通信 |
| HTTPS | 加密传输 | 安全敏感数据 |
高并发请求处理: