OkHttp网络框架设计
# 03.OkHttp网络框架设计
# 目录介绍
- 01.整体概述介绍
- 1.1 概述介绍
- 1.2 核心特性说明
- 1.3 技术架构概览
- 1.4 问题思考
- 02.核心架构设计
- 2.1 整体架构设计
- 2.2 整体设计思路
- 2.3 核心组件关系图
- 03.核心组件详解
- 3.1 OkHttpClient
- 3.2 Request请求封装
- 3.3 Call请求执行接口
- 3.4 Dispatcher调度器
- 3.5 拦截器机制
- 3.6 Response返回
- 04.核心流程分析
- 4.1 请求执行流程
- 4.2 连接管理流程
- 4.3 缓存处理流程
- 05.关键技术特性
- 5.1 连接池管理
- 5.2 HTTP/2支持
- 5.3 缓存机制设计
- 5.4 安全特性设计
- 5.5 SSL/TLS流程
- 06.性能优化策略
- 6.1 连接复用
- 6.2 请求合并
- 6.3 压缩传输
- 6.4 内存管理
- 07.使用示例说明
- 7.1 基本使用
- 7.2 高级配置
- 7.3 统计请求耗时
- 08.最佳实践
- 8.1 客户端复用
- 8.2 请求取消
- 8.3 错误处理
- 09.架构优势
- 9.1 设计模式应用
- 9.2 SOLID原则体现
- 9.3 性能优势
- 10.优秀代码设计深度解析
- 10.1 责任链模式的极致运用
- 10.2 不可变对象与建造者模式
- 10.3 Dispatcher调度器的设计精髓
- 10.4 连接池的引用计数设计
- 10.5 Okio的Segment池化设计
- 10.6 设计思想总结
# 01.整体概述介绍
# 1.1 概述介绍
OkHttp是一个高效的HTTP客户端库,专为Android和Java应用程序设计。基于OkHttpLib模块的源码分析,详细阐述了OkHttp的架构设计、核心组件、工作流程和技术特性。避免陷入代码细节,着重理解设计思想!
# 1.2 核心特性说明
- 高效的HTTP/2支持:完整支持HTTP/2协议,包括连接复用、服务器推送等特性
- 连接池管理:自动管理连接池,减少连接建立的开销
- 透明的GZIP压缩:自动处理响应压缩,减少网络传输量
- 响应缓存:支持HTTP缓存,避免重复请求
- 请求/响应拦截:灵活的拦截器机制,支持请求和响应的定制化处理
- 故障恢复:自动重试机制,提高网络请求的可靠性
# 1.3 技术架构概览
# 1.4 问题思考
- OkHttp设计:OkHttp整体设计思路是什么样的?request和respond分别如何设计?如何设计call请求操作?
- OkHttp同步异步:如何设计同步和异步请求?同步操作做了什么?异步操作如何处理逻辑?
- OkHttp拦截器:拦截器是如何设计的?为什么需要拦截器?拦截器如何处理拦截,和向下分发逻辑?如何做重试机制的设计?
- OkHttp缓存:如何设计缓存?什么情况下会用到网络缓存?缓存拦截器的核心思想是什么?
- OkHttp分发器:同步和异步请求的Dispatcher是如何调度的?设计的巧妙之处是什么?
- OkHttp线程池:使用了什么线程池?是如何管理线程任务?跟普通线程池使用有何区别?
# 02.核心架构设计
# 2.1 整体架构设计
OkHttp采用分层架构设计,主要包括以下几个层次:
- 应用接口层:提供简洁的API接口,包括OkHttpClient、Request、Response等
- 调度管理层:Dispatcher负责请求的调度和线程池管理
- 拦截器链层:责任链模式实现的拦截器机制
- 连接管理层:ConnectionPool管理HTTP连接的复用
- 网络传输层:底层的Socket连接和数据传输
# 2.2 整体设计思路
网络请求到响应大概流程图如下所示

整体设计思路大概如下所示:
- 第一步:创建OkHttpClient对象,由于创建这个对象十分复杂,因此采用builder设计模式构造
- 第二步:包装Request请求体对象,主要是存放url,header,get请求,post请求等等属性
- 第三步:通过newCall(request)去创建一个call请求
- 第四步:开始执行同步execute或者enqueue请求,这里会使用到线程池
- 第五步:添加各种拦截器,缓存拦截器,
- 第六步:处理缓存拦截,数据复用的技术逻辑
- 第七步:创建连接请求的操作,给服务端发送请求
- 第八步:获取返回response数据,这里主要是处理code和body数据
# 2.3 核心组件关系图
# 03.核心组件详解
# 3.1 OkHttpClient
OkHttpClient是整个框架的入口点,采用建造者模式进行配置:
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
final Dispatcher dispatcher;
final @Nullable Proxy proxy;
final List<Protocol> protocols;
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors;
final List<Interceptor> networkInterceptors;
final EventListener.Factory eventListenerFactory;
final ProxySelector proxySelector;
final CookieJar cookieJar;
final @Nullable Cache cache;
final @Nullable InternalCache internalCache;
final SocketFactory socketFactory;
final SSLSocketFactory sslSocketFactory;
final ConnectionPool connectionPool;
// ... 更多配置项
}
创建OkHttpClient对象,主要是用于Api网络请求的对象。类似于初始化网络请求,可以设置超时时间,日志打印拦截器,代理,ssl校验,域名校验等等。
主要职责:
- 管理全局配置(超时、代理、SSL等)
- 创建Call对象
- 管理拦截器链
- 管理连接池和缓存
# 3.1.1 创建流程架构图
# 3.1.2 Builder模式原理
OkHttpClient采用Builder模式的主要原因:
- 参数众多:OkHttpClient有30+个配置参数
- 可选配置:大部分参数都有合理的默认值
- 不可变性:创建后的OkHttpClient实例不可修改
- 链式调用:提供流畅的API体验
- 参数验证:在build()时统一验证参数合法性
# 3.1.3 Builder配置流程图
# 3.1.4 超时配置关系图
在创建对象时,连接超时,读取超时,写入超时,完整请求超时,都可以灵活配置。那么这块是如何设计的呢?
# 3.2 Request请求封装
主要是封装一个Request请求体。
Request request = new Request.Builder()
.url(url)
.addHeader("cookie","yangchong")
.get()
.build();
Request类封装了HTTP请求的所有信息:
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;
public static class Builder {
@Nullable HttpUrl url;
String method;
Headers.Builder headers;
@Nullable RequestBody body;
Map<Class<?>, Object> tags = Collections.emptyMap();
}
}
Request包括Headers和RequestBody,而RequestBody是abstract的,他的子类是有FormBody(表单提交的)和MultipartBody(文件上传),分别对应了两种不同的MIME类型。
FormBody :"application/x-www-form-urlencoded"
MultipartBody:"multipart/"+xxx.
核心特性:
- 不可变对象设计,线程安全
- 建造者模式构建
- 支持多种HTTP方法(GET、POST、PUT、DELETE等)
- 灵活的Header管理
# 3.3 Call请求执行接口
如何设计Call请求基于接口开发,设计了Call接口,里面主要做同步请求execute,异步请求enqueue,取消请求cancel等等。
Call类详解:有道词典翻译该类注释:调用是准备执行的请求。call可以取消。由于此对象表示单个请求/响应对(流),因此不能执行两次。
主要是HTTP请求任务封装
- 可以说我们能用到的操纵基本上都定义在这个接口里面了,可以通过Call对象来操作请求,同步请求execute,异步请求enqueue。
- 而Call接口内部提供了Factory工厂方法模式(将对象的创建延迟到工厂类的子类去进行,从而实现动态配置)。
Call接口定义了请求执行的标准:
public interface Call extends Cloneable {
Request request();
Response execute() throws IOException;
void enqueue(Callback responseCallback);
void cancel();
boolean isExecuted();
boolean isCanceled();
Timeout timeout();
Call clone();
}
RealCall实现:
- 同步执行:
execute()方法直接在当前线程执行 - 异步执行:
enqueue()方法提交到线程池执行 - 请求取消:支持请求的取消操作
# 3.3.1 核心方法对比
| 特性 | execute() | enqueue() |
|---|---|---|
| 执行方式 | 同步阻塞 | 异步非阻塞 |
| 线程模型 | 当前线程执行 | 线程池执行 |
| 返回方式 | 直接返回Response | 通过Callback回调 |
| 异常处理 | 抛出IOException | 通过Callback.onFailure |
| 调度管理 | 简单记录 | 复杂的队列调度 |
# 3.3.2 执行流程架构图
# 3.3.3 Execute执行时序图
# 3.3.4 Enqueue执行时序图
# 3.4 Dispatcher调度器
网络请求肯定涉及多线程,如何处理大量任务?采用Dispatcher作为调度,与线程池配合实现了高并发,低阻塞的的运行。针对请求任务,采用Deque作为集合,按照入队的顺序先进先出。
Dispatcher负责管理请求的执行调度:
public final class Dispatcher {
private int maxRequests = 64; // 最大并发请求数
private int maxRequestsPerHost = 5; // 单主机最大并发数
// 等待执行的异步请求队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
// 正在执行的异步请求队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
// 正在执行的同步请求队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
}
调度策略:
- 最大并发请求数限制(默认64个)
- 单主机最大并发数限制(默认5个)
- 使用双端队列管理等待和运行中的请求
- 自动的线程池管理
# 3.4.1 Dispatcher架构图
# 3.4.2 异步调度架构
# 3.4.3 调度策略流程图
# 3.5 拦截器机制
OKHttp有两种调用方式,一种是阻塞的同步请求,一种是异步的非阻塞的请求。
但是无论同步还是异步都会调用下RealCall的 getResponseWithInterceptorChain方法来完成请求,同时将返回数据或者状态通过Callback来完成。
设计拦截器的核心原理
Interceptor 负责拦截和分发。先来看看含义:观察,修改以及可能短路的请求输出和响应请求的回来。通常情况下拦截器用来添加,移除或者转换请求或者回应的头部信息。
拦截器,就像水管一样,把一节一节的水管(拦截器)连起来,形成一个回路。实际上client到server也是如此,通过一个又一个的interceptor串起来,然后把数据发送到服务器,又能接受返回的数据,每一个拦截器(水管)都有自己的作用,分别处理不同东西,比如消毒,净化,去杂质,就像一层层过滤网一样。
接口是如何设计的
public interface Interceptor {
//负责拦截
Response intercept(Chain chain) throws IOException;
interface Chain {
//负责分发、前行
Response proceed(Request request) throws IOException;
}
}
# 3.5.1 拦截器时序图
OkHttp的拦截器采用责任链模式,每个拦截器负责特定的功能:
# 3.5.2 拦截器类关系图
# 3.5.3 内置拦截器详解
- 应用拦截器(Application Interceptors)
- 用户自定义的拦截器
- 在请求发送前和响应接收后执行
- 可以修改请求和响应
- 重试和重定向拦截器(RetryAndFollowUpInterceptor)
- 处理请求失败重试
- 处理HTTP重定向
- 管理连接的获取和释放
- 桥接拦截器(BridgeInterceptor)
- 将用户请求转换为网络请求
- 添加必要的HTTP头(Content-Type、Content-Length等)
- 处理Cookie
- 处理GZIP压缩
- 缓存拦截器(CacheInterceptor)
- 实现HTTP缓存策略
- 处理缓存的读取和写入
- 支持强制缓存和协商缓存
- 连接拦截器(ConnectInterceptor)
- 建立与服务器的连接
- 管理连接池
- 处理HTTP/2连接复用
- 网络拦截器(Network Interceptors)
- 用户自定义的网络级拦截器
- 在实际网络请求前后执行
- 调用服务器拦截器(CallServerInterceptor)
- 实际的网络IO操作
- 发送请求数据
- 读取响应数据
# 3.5.4 重定向拦截器
核心功能
- 异常恢复:处理网络异常和连接失败
- 重定向处理:自动处理HTTP重定向响应
- 重试逻辑:根据策略决定是否重试请求
# 3.5.6 缓存拦截器
核心功能
- 缓存策略:根据HTTP缓存规范决定缓存行为
- 缓存读取:从缓存中读取有效的响应
- 缓存写入:将可缓存的响应写入缓存
- 条件请求:发送If-None-Match、If-Modified-Since等条件请求
# 3.5.7 网络请求拦截器
CallServerInterceptor(网络请求拦截器)
- 请求发送:将HTTP请求发送到服务器
- 响应接收:接收服务器的HTTP响应
- 流控制:管理请求和响应的数据流
- 协议处理:处理HTTP/1.1和HTTP/2的具体协议细节
# 3.5.8 拦截器链状态管理
# 3.6 Response返回
Response核心职责
- 响应封装:统一封装HTTP响应的所有信息
- 流式处理:支持大文件的流式读取和处理
- 内存管理:优化内存使用,避免大响应体的内存溢出
- 协议适配:支持HTTP/1.1和HTTP/2协议的响应处理
- 缓存集成:与缓存系统无缝集成
# 3.6.1 类层次结构图
# 3.6.2 创建流程架构图
Response创建流程架构图
# 3.6.3 状态行解析流程图
# 3.6.4 Headers解析流程
# 3.6.5 ResponseBody架构设计
# 3.6.6 ResponseBody数据读取
# 3.6.7 Response生命周期
# 3.6.8 响应数据处理
# 3.6.9 Response错误处理
# 3.6.10 Response缓存机制
# 04.核心流程分析
# 4.1 请求执行流程
# 4.2 连接管理流程
# 4.3 缓存处理流程
# 05.关键技术特性
# 5.1 连接池管理
OkHttp使用连接池来复用HTTP连接,提高性能:
public final class ConnectionPool {
private static final Executor executor = new ThreadPoolExecutor(
0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
Util.threadFactory("OkHttp ConnectionPool", true)
);
private final RealConnectionPool delegate;
public ConnectionPool() {
this(5, 5, TimeUnit.MINUTES);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.delegate = new RealConnectionPool(taskRunner, maxIdleConnections, keepAliveDuration, timeUnit);
}
}
连接池特性:
- 默认最大空闲连接数:5个
- 默认连接保活时间:5分钟
- 自动清理过期连接
- 支持HTTP/1.1和HTTP/2连接复用
# 5.2 HTTP/2支持
OkHttp完整支持HTTP/2协议:
- 多路复用:单个连接上并发处理多个请求
- 服务器推送:服务器主动推送资源
- 头部压缩:HPACK算法压缩HTTP头部
- 流量控制:精确控制数据传输速率
# 5.2.1 HTTP/2连接架构
# 5.3 缓存机制设计
实现完整的HTTP缓存策略:
public final class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Response cacheCandidate = cache != null ? cache.get(chain.request()) : null;
long now = System.currentTimeMillis();
CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;
// 缓存策略处理逻辑
if (networkRequest == null && cacheResponse == null) {
return new Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(504)
.message("Unsatisfiable Request (only-if-cached)")
.build();
}
if (networkRequest == null) {
return cacheResponse.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build();
}
// 继续网络请求...
}
}
# 5.3.1 缓存架构设计
# 5.3.2 缓存核心组件
# 5.3.3 缓存决策流程
# 5.3.4 磁盘缓存设计
DiskLruCache是OkHttp缓存的核心存储引擎,采用LRU(Least Recently Used)算法:
# 5.3.5 缓存拦截器思想
CacheInterceptor核心流程
# 5.4 安全特性设计
在安全性方面采用了多层防护策略,涵盖传输层安全(TLS)、证书验证、主机名校验、数据完整性保护等多个维度。
- TLS支持:支持TLS 1.2和1.3
- 证书锁定:Certificate Pinning防止中间人攻击
- 主机名验证:严格的主机名验证
- 协议降级保护:防止协议降级攻击
# 5.4.1 安全组件架构图
# 5.4.2 安全数据流图
# 5.4.3 证书验证机制
OkHttp使用CertificateChainCleaner来清理和验证证书链:
public abstract class CertificateChainCleaner {
public abstract List<Certificate> clean(List<Certificate> chain, String hostname)
throws SSLPeerUnverifiedException;
public static CertificateChainCleaner get(X509TrustManager trustManager) {
return Platform.get().buildCertificateChainCleaner(trustManager);
}
public static CertificateChainCleaner get(X509TrustManager trustManager, TrustRootIndex trustRootIndex) {
return new BasicCertificateChainCleaner(trustRootIndex);
}
}
证书链验证流程
# 5.4.4 主机名验证
OkHostnameVerifier实现,OkHttp实现了符合RFC 2818标准的主机名验证器
# 5.4.5 中间人攻击防护
OkHttp通过多层防护机制防止中间人攻击:
# 5.5 SSL/TLS流程
SSL配置层次图
# 5.5.1 TLS版本支持
OkHttp支持多个TLS版本,并提供了灵活的配置机制:
public enum TlsVersion {
TLS_1_3("TLSv1.3"), // jdk11+, android10+
TLS_1_2("TLSv1.2"), // jdk7+, android16+
TLS_1_1("TLSv1.1"), // jdk7+, android16+
TLS_1_0("TLSv1.0"), // jdk7+, android16+
SSL_3_0("SSLv3"); // jdk6+, android9+
public static final TlsVersion[] DEFAULT = {
TLS_1_3, TLS_1_2, TLS_1_1, TLS_1_0
};
}
# 5.5.2 TLS版本策略
# 5.5.3 TLS握手过程
RealConnection负责建立安全的TLS连接:
# 06.性能优化策略
# 6.1 连接复用
# 6.1.1 连接复用架构
# 6.2 请求合并
对于相同的请求,OkHttp会自动合并,避免重复请求。
# 6.3 压缩传输
- 自动GZIP压缩响应体
- 支持Brotli压缩算法
- 透明的压缩/解压缩处理
# 6.4 内存管理
- 使用Okio进行高效的IO操作
- 智能的缓冲区管理
- 及时释放不需要的资源
# 6.4.1 内存管理架构
# 6.4.2 IO操作架构
# 07.使用示例说明
# 7.1 基本使用
// 创建客户端
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
// 构建请求
Request request = new Request.Builder()
.url("https://api.example.com/data")
.header("Authorization", "Bearer token")
.build();
// 同步执行
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
String result = response.body().string();
// 处理响应数据
}
}
// 异步执行
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// 处理失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String result = response.body().string();
// 处理响应数据
}
}
});
# 7.2 高级配置
OkHttpClient client = new OkHttpClient.Builder()
// 连接池配置
.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
// 缓存配置
.cache(new Cache(cacheDir, 50 * 1024 * 1024)) // 50MB缓存
// 拦截器
.addInterceptor(new LoggingInterceptor())
.addNetworkInterceptor(new NetworkInterceptor())
// 重试配置
.retryOnConnectionFailure(true)
// 代理配置
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy.example.com", 8080)))
// SSL配置
.sslSocketFactory(sslSocketFactory, trustManager)
.hostnameVerifier(hostnameVerifier)
.build();
# 7.3 统计请求耗时
OkHttp如何进行各个请求环节的耗时统计呢?
- OkHttp 版本提供了EventListener接口,可以让调用者接收一系列网络请求过程中的事件,例如DNS解析、TSL/SSL连接、Response接收等。
- 通过继承此接口,调用者可以监视整个应用中网络请求次数、流量大小、耗时(比如dns解析时间,请求时间,响应时间等等)情况。
如何消耗记录时间
- 在OkHttp库中有一个EventListener类。该类是网络事件的侦听器。扩展这个类以监视应用程序的HTTP调用的数量、大小和持续时间。
- 所有启动/连接/获取事件最终将接收到匹配的结束/释放事件,要么成功(非空参数),要么失败(非空可抛出)。
- 比如,可以在开始链接记录时间;dns开始,结束等方法解析记录时间,可以计算dns的解析时间。
- 比如,可以在开始请求记录时间,记录connectStart,connectEnd等方法时间,则可以计算出connect连接时间。
OkHttp性能监控架构
# 10.优秀代码设计深度解析
# 10.1 责任链模式的极致运用
OkHttp的拦截器是责任链模式最经典的应用之一,它的设计值得深入分析:
// RealInterceptorChain — 责任链的核心实现
// 精妙之处在于每次proceed()创建新的Chain,index+1
class RealInterceptorChain(
val interceptors: List<Interceptor>,
val index: Int, // 当前拦截器索引
val exchange: Exchange?,
val request: Request
) : Interceptor.Chain {
override fun proceed(request: Request): Response {
// 核心:创建新的Chain,index+1,传给下一个拦截器
val next = RealInterceptorChain(
interceptors, index + 1, exchange, request
)
val interceptor = interceptors[index]
// 调用拦截器的intercept方法
// 拦截器内部调用chain.proceed()时,
// 实际上调用的是next(index+1)的proceed
val response = interceptor.intercept(next)
return response
}
}
// 设计精髓分析:
// 1. 不可变链:每次proceed创建新Chain而非修改index
// → 线程安全,无状态竞争
// → 同一个拦截器可以多次调用proceed(重试场景)
// 2. 拦截器可以选择:
// a. 修改request → chain.proceed(modifiedRequest)
// b. 短路返回 → 直接return response(不调用proceed)
// c. 修改response → 先proceed获取response,再修改
// d. 重试 → 多次调用chain.proceed()
// 3. 应用拦截器 vs 网络拦截器的区别:
// 应用拦截器在最外层 → 只执行一次(即使有重试/重定向)
// 网络拦截器在内层 → 每次实际网络请求都会执行
责任链中拦截器的协作设计:
请求方向 →
App拦截器 → Retry → Bridge → Cache → Connect → Network → CallServer
↓
响应方向 ← 服务器
App拦截器 ← Retry ← Bridge ← Cache ← Connect ← Network ← CallServer
每个拦截器的职责:
RetryAndFollowUpInterceptor:
→ 请求:记录当前请求信息
→ 响应:检查是否需要重试/重定向
→ 重试时重新调用chain.proceed()(新一轮责任链)
→ 最大重试次数:20次
BridgeInterceptor:
→ 请求:添加Content-Type, Content-Length, Host, User-Agent等Header
→ 请求:添加Accept-Encoding: gzip(透明压缩)
→ 请求:添加Cookie
→ 响应:自动解压gzip(如果Accept-Encoding是自动添加的)
→ 响应:保存Cookie
CacheInterceptor:
→ 请求:查询缓存,计算缓存策略
→ 可能短路:缓存有效时直接返回,不调用proceed
→ 响应:将可缓存的响应写入磁盘缓存
ConnectInterceptor:
→ 请求:从连接池获取或创建连接
→ 执行TLS握手
→ 不处理响应(直接透传)
CallServerInterceptor(链尾):
→ 请求:通过Socket发送HTTP请求
→ 响应:从Socket读取HTTP响应
→ 不调用proceed(它是最后一个拦截器)
# 10.2 不可变对象与建造者模式
// Request — 不可变对象设计典范
public final class Request {
final HttpUrl url; // final字段
final String method; // 不可修改
final Headers headers; // Headers也是不可变的
final @Nullable RequestBody body;
final Map<Class<?>, Object> tags;
// 构造函数私有,只能通过Builder创建
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build(); // build()返回不可变对象
this.body = builder.body;
this.tags = Util.immutableMap(builder.tags); // 不可变Map
}
// 如果需要修改,创建新的Builder
public Builder newBuilder() {
return new Builder(this); // 基于当前Request创建Builder
}
}
// Headers — 高效的不可变头信息存储
public final class Headers {
private final String[] namesAndValues; // 扁平数组存储,比Map更紧凑
// 不使用Map<String, String>的原因:
// 1. HTTP Header的Key可以重复(如Set-Cookie)
// 2. 扁平数组内存更紧凑,无HashMap的开销
// 3. 数组遍历比HashMap遍历更快(缓存友好)
// 4. 数组是不可变的(创建后不能修改长度和内容)
public String get(String name) {
for (int i = namesAndValues.length - 2; i >= 0; i -= 2) {
if (name.equalsIgnoreCase(namesAndValues[i])) {
return namesAndValues[i + 1];
}
}
return null;
// 从后往前找:后设置的Header优先
}
}
// 设计优势:
// 1. 线程安全:不可变对象天然线程安全,无需synchronized
// 2. 可共享:多个线程可以安全地共享同一个Request对象
// 3. 缓存友好:可以安全地用作Map的Key
// 4. 可预测:创建后状态不会改变,便于调试
# 10.3 Dispatcher调度器的设计精髓
// Dispatcher — 精妙的线程池设计
public final class Dispatcher {
// 线程池使用SynchronousQueue而非LinkedBlockingQueue
private @Nullable ExecutorService executorService;
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(
0, // 核心线程数为0
Integer.MAX_VALUE, // 最大线程数无限
60, TimeUnit.SECONDS, // 空闲60秒回收
new SynchronousQueue<>(), // 关键!
Util.threadFactory("OkHttp Dispatcher", false)
);
}
return executorService;
}
}
// 设计分析 — 为什么用SynchronousQueue?
//
// SynchronousQueue是零容量队列:
// → 不能存储任何任务
// → 每次put必须有对应的take(生产者直接交给消费者)
// → 如果没有空闲线程 → 创建新线程
//
// 与LinkedBlockingQueue对比:
// LinkedBlockingQueue:任务先入队列 → 线程从队列取任务
// → 如果核心线程忙,任务堆积在队列中
// → 不会创建新线程(除非队列满了)
//
// SynchronousQueue + maxPoolSize=MAX_VALUE:
// → 任务提交时,如果有空闲线程就用,没有就立即创建新线程
// → 永远不会排队等待(网络请求需要及时执行,排队会增加延迟)
// → 空闲60秒后线程自动回收(不浪费资源)
//
// 为什么不直接Executors.newCachedThreadPool()?
// 因为OkHttp需要在Dispatcher层面控制最大并发数(64)和单Host并发数(5)
// CachedThreadPool无法控制并发数上限
// 双端队列(Deque)设计:
// readyAsyncCalls + runningAsyncCalls 构成了二级调度
// ready → running 的转换由promoteAndExecute()控制
// 这种设计让OkHttp可以灵活控制并发策略
// 而不是将所有请求都提交给线程池
// promoteAndExecute()的精妙之处:
// 1. 遍历readyAsyncCalls
// 2. 检查runningAsyncCalls.size() < maxRequests
// 3. 检查同一Host的请求数 < maxRequestsPerHost
// 4. 满足条件 → 移到running队列 → 提交线程池
// 5. 请求完成 → finished() → 再次调用promoteAndExecute()
// → 形成自驱动的调度循环
# 10.4 连接池的引用计数设计
ConnectionPool的连接管理 — 引用计数 + 弱引用
连接池的核心挑战:
何时判断一个连接可以被回收?
→ 一个连接可能被多个请求共享(HTTP/2多路复用)
→ 不能简单地用空闲时间判断
OkHttp的解决方案:引用计数
RealConnection中的引用管理:
val calls = mutableListOf<Reference<RealCall>>()
// 每个使用该连接的RealCall会添加到列表中(弱引用)
// 当RealCall完成时,从列表中移除
// 当calls为空 → 连接空闲
连接清理线程的工作逻辑:
fun cleanup(now: Long): Long {
var longestIdleDurationNs = -1L
var idleConnectionCount = 0
var longestIdleConnection: RealConnection? = null
for (connection in connections) {
// 检查连接是否在使用中
if (pruneAndGetAllocationCount(connection) > 0) {
continue // 还在使用,跳过
}
idleConnectionCount++
val idleDurationNs = now - connection.idleAtNanos
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs
longestIdleConnection = connection
}
}
when {
longestIdleDurationNs >= keepAliveDurationNs
|| idleConnectionCount > maxIdleConnections -> {
// 空闲时间超限 或 空闲连接数超限 → 关闭最久空闲的连接
connections.remove(longestIdleConnection)
}
idleConnectionCount > 0 -> {
// 有空闲连接但未超限 → 等待到超限时间再检查
return keepAliveDurationNs - longestIdleDurationNs
}
else -> {
// 没有空闲连接 → 等待下次put时唤醒
return -1
}
}
}
设计精髓:
1. 弱引用管理RealCall → 即使RealCall泄漏也不影响连接回收
2. 清理线程按需唤醒 → put新连接时唤醒,不空转
3. 双条件清理 → 时间超限或数量超限都触发
4. 返回下次清理的等待时间 → 精确的延迟清理
# 10.5 Okio的Segment池化设计
Okio — OkHttp的IO基础库,性能优化的底层支撑
Segment(段)设计:
Okio不使用大的连续byte数组,而是使用Segment链表:
Buffer = Segment1 ←→ Segment2 ←→ Segment3 ←→ ...
每个Segment:
├── byte[] data = new byte[8192] // 固定8KB
├── int pos // 读取位置
├── int limit // 写入位置
├── Segment next, prev // 双向链表指针
├── boolean shared // 是否共享
└── boolean owner // 是否拥有data数组
Segment池化(SegmentPool):
static final SegmentPool INSTANCE = new SegmentPool();
// 池容量:最多缓存64KB(8个Segment)
// 使用单链表实现(next指针复用)
Segment take() {
// 从池中取出一个Segment
// 如果池为空 → 创建新Segment
}
void recycle(Segment segment) {
// 将用完的Segment归还到池中
// 如果池满 → 丢弃(让GC回收)
// 归还前重置pos和limit
}
零拷贝操作:
Okio支持Segment级别的移动而非字节拷贝:
buffer1 → buffer2 传输数据:
传统方式:从buffer1读byte[] → 写入buffer2 → 两次拷贝
Okio方式:将Segment从buffer1的链表移到buffer2 → 零拷贝!
前提:Segment的owner标记确保安全
ByteString — 不可变字节串:
类似String但存储byte[]
→ 不可变 → 可安全共享
→ 内置SHA-256、MD5等计算方法
→ 支持Base64、Hex编解码
→ OkHttp的TLS握手、证书处理大量使用
设计启发:
1. 分段存储避免大内存分配 → 减少GC压力
2. 池化复用减少对象创建 → 更少的内存分配
3. 链表结构支持零拷贝 → 更高的IO效率
4. 这就是为什么OkHttp的IO性能优于HttpURLConnection
# 10.6 设计思想总结
OkHttp优秀设计思想汇总:
1. 分层架构(Layered Architecture)
应用层 → 调度层 → 拦截器层 → 连接层 → IO层
每层职责清晰,可独立测试和替换
2. 责任链模式(Chain of Responsibility)
拦截器链是OkHttp最核心的设计
→ 每个拦截器关注一个方面
→ 添加新功能只需添加拦截器,不修改已有代码
→ 完美体现开闭原则
3. 不可变对象(Immutable Objects)
Request、Response、Headers都是不可变的
→ 线程安全无需加锁
→ 可以安全地在拦截器链中传递
4. 建造者模式(Builder Pattern)
OkHttpClient、Request、Response都使用Builder
→ 参数众多但创建简洁
→ newBuilder()支持基于已有对象微调
5. 连接复用(Connection Pooling)
→ 引用计数管理连接生命周期
→ 按需清理空闲连接
→ HTTP/2多路复用同一连接
6. 异步调度(Async Dispatch)
→ Dispatcher双队列调度
→ SynchronousQueue实现即时执行
→ 细粒度的并发控制
7. 透明处理(Transparent Processing)
→ 自动重试、重定向
→ 自动GZIP压缩/解压
→ 自动Cookie管理
→ 对上层透明,减少使用复杂度
8. 可扩展性(Extensibility)
→ 应用拦截器和网络拦截器
→ EventListener监听各阶段事件
→ 可替换的DNS解析器、SSL配置等
这些设计思想不仅适用于网络框架,
对任何复杂系统的架构设计都有参考价值。