一、核心架构与设计模式
- 拦截器链(Interceptor Chain)
• 责任链模式:通过RealInterceptorChain
按顺序执行拦截器,每个拦截器可修改请求/响应
• 五大核心拦截器(按执行顺序):
-
RetryAndFollowUpInterceptor:处理重定向和失败重试
◦ 自动重试机制:通过
StreamAllocation
寻找新的路由(Route)和连接(Connection)◦ 最多重试次数:20次(源码常量
MAX_FOLLOW_UPS
) -
BridgeInterceptor:补全请求头(User-Agent, Cookie等)
◦ 自动添加
Content-Type
和Content-Length
等必要头信息 -
CacheInterceptor:缓存管理(基于HTTP缓存协议)
◦ 使用
CacheStrategy
判断是否使用缓存(根据Cache-Control
头) -
ConnectInterceptor:建立网络连接
◦ 关键类
RealConnection
:封装TCP/TLS连接,复用连接的关键 -
CallServerInterceptor:发送请求并读取响应
◦ 通过
HttpCodec
处理HTTP协议编解码 -
连接池(ConnectionPool)
• 复用机制:通过
ConnectionPool
管理空闲连接(默认最大空闲连接数5,存活时间5分钟)
java
public ConnectionPool(int maxIdleConnections, long keepAliveDuration) {
this.maxIdleConnections = maxIdleConnections; // 默认5
this.keepAliveDurationNs = keepAliveDuration; // 默认5分钟
}
• 连接复用条件:相同Host + 相同SocketFactory + 相同代理配置
• LRU清理策略:后台线程定期清理过期/空闲连接
二、异步请求与线程管理
高频考点:Dispatcher 工作机制、线程池配置
- Dispatcher 调度器
• 异步请求队列:维护两个队列
• runningAsyncCalls
:正在执行的异步请求(默认最大64)
• readyAsyncCalls
:等待执行的异步请求
• executorService
:线程池(核心线程数0,最大线程数Integer.MAX_VALUE)
• 最大并发请求数:默认同一域名最大5个并发(通过 Dispatcher.setMaxRequestsPerHost
设置)
线程池优化
• CachedThreadPool:避免频繁创建/销毁线程
java
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;
}
2.同步请求队列 runningSyncCalls
三、关键源码解析
高频考点:同步/异步请求流程、连接复用实现
- 请求执行流程
• 同步请求:
java
// OkHttpClient.newCall(request).execute()
public Response execute() {
synchronized (this) {
// 检查是否已执行
executed = true;
}
transmitter.timeoutEnter(); // 超时管理
transmitter.prepareToConnect(request);
// 加入运行队列
client.dispatcher().executed(this);
// 执行拦截器链
return getResponseWithInterceptorChain();
}
• 异步请求:
enqueue实际上是new了一个RealCall的内部类AsyncCall扔进了dispatcher中,如果当前正在运行的异步请求数小于阈值maxRequests (默认Dispatcher中为64)并且同host下运行的请求小于阈值maxRequestsPerHost(默认Dispatcher中为5),就将AsyncCall添加到正在运行的异步队里,并通过线程池异步执行,否则就将其丢到等待队列排队。
java
// OkHttpClient.newCall(request).enqueue(callback)
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute(); // 调度执行
}
- 连接复用实现
• RealConnection 复用逻辑:
java
// StreamAllocation.findConnection()
if (connection != null) {
releasedConnection = this.connection;
result = releasedConnection;
releasedConnection = null;
if (!result.isEligible(address.url().host(), address.url().host())) {
result = null;
}
}
四、高频面试题与答案
-
OkHttp如何实现连接复用?
• 通过ConnectionPool管理空闲连接,复用相同Host+Port的连接
• 使用
Connection: keep-alive
头实现HTTP/1.1长连接 -
拦截器执行顺序是怎样的?
• RetryAndFollowUp → Bridge → Cache → Connect → CallServer
-
如何优化大文件下载?
• 使用
OkHttpClient
配置连接/读取超时• 通过拦截器实现分块下载(
Range
头) -
如何处理SSL/TLS握手?
• 使用
SSLSocketFactory
和HostnameVerifier
• 证书锁定(Certificate Pinning)示例:
javaCertificatePinner pinner = new CertificatePinner.Builder() .add("publicobject.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAA=") .build();
-
使用到的设计模式
• 策略模式(在CacheInterceptor中,在响应数据的选择中使用了策略模式,选择缓存数据还是选择网络访问。)
• 责任链模式(拦截器的链式调用)
五、总结
OkHttp面试题核心要点:
- 拦截器链责任链模式的执行顺序与作用
- 连接池管理策略(LRU、最大空闲数)
- Dispatcher异步调度机制与线程池配置
- 缓存策略(Cache-Control与磁盘缓存)