1 简述
OKhttp3是一款高效的Http客户端,支持连接同一地址的链接共享同一个socket,通过连接池减少响应延迟,还有透明的GZIP压缩,请求缓存等优势,其核心主要有路由、连接协议、拦截器、代理、安全性认证、连接池、以及网络适配,移除或者转换请求或者回应的头部信息。
上张简易的流程图,我们再来看OkHttp3的原理。
注:本文分析均基于"com.squareup.okhttp3:okhttp:3.14.9"
版本。
2 基本使用
下面是使用OkHttp进行GET请求的一个示例。
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
.url("https://api.github.com/")
.build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String result = response.body().string();
}
});
从这个示例中,我们不难看出官方实现的一种设计美感在里面。
第一步,OkHttpClient okHttpClient = new OkHttpClient();
我们把它类比为得到一个邮箱。
第二步,我们开始写信,new Request.Builder().url("https://api.github.com/").build();
信里面我们可以添加很多内容,以及最重要的寄信地址 URL
。
第三步,我们把信放入信箱,okHttpClient.newCall(request);
第四步,等待返回结果。
OkHttp设计很好的一个点就是开发者不用关心,请求怎么发出去?大量请求怎么维护的?返回结果如何处理?
只需要,“写好信”
放过去,然后等待(异步\同步)返回结果即可。
3 原理剖析
下面我们以三个问题来看看OkHttp核心逻辑做了哪些事?
- 请求去了哪里?
- 大量请求如何被维护的?
- 请求如何被处理?返回如何被接收?
3.1 请求去了哪里?大量请求如何被维护的?
从call.enqueue(new Callback()
往下跟源码,就会找到一个Dispatcher
的类。
在这个类中有几个队列:
/** 异步请求的执行顺序的队列 */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** 运行中的异步请求队列 */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** 运行中的同步请求队列(包含已取消,但是还没有结束的请求) */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
看代码我们就会发现,所有请求都在着三个队列中流转(注:线程池中的先不看)。
以异步为例:
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call); // 请求顺序加入队列
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
if (!call.get().forWebSocket) {
AsyncCall existingCall = findExistingCallWithHost(call.host());
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
}
}
promoteAndExecute();
}
promoteAndExecute() 方法
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
//获取等待中的任务队列
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
// 超过可以同时运行的最大请求任务数64
if (runningAsyncCalls.size() >= maxRequests) break;
// 超过同一主机同时运行的最大请求任务数5
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue;
// 满足【运行队列任务数小于64】和【同一主机同时运行请求小于5】
// 就从等待队列中移除,添加到【运行时队列中】
i.remove();
asyncCall.callsPerHost().incrementAndGet();
executableCalls.add(asyncCall);
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
// 执行每一个异步请求
asyncCall.executeOn(executorService());
}
return isRunning;
}
通过源码,我们了解到,异步请求均被放在了
readyAsyncCalls
,然后根据条件放到runningAsyncCalls
中,最后调用线程池执行每一个异步请求!
// 具体执行源码
void executeOn(ExecutorService executorService) {
assert (!Thread.holdsLock(client.dispatcher()));
boolean success = false;
try {
// 放到线程池执行
executorService.execute(this);
success = true;
} catch (RejectedExecutionException e) {
InterruptedIOException ioException = new InterruptedIOException("executor rejected");
ioException.initCause(e);
transmitter.noMoreExchanges(ioException);
responseCallback.onFailure(RealCall.this, ioException);
} finally {
if (!success) {
client.dispatcher().finished(this); // This call is no longer running!
}
}
}
// NamedRunnable 中调用
@Override protected void execute() {
boolean signalledCallback = false;
transmitter.timeoutEnter();
try {
Response response = getResponseWithInterceptorChain();
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} catch (Throwable t) {
cancel();
if (!signalledCallback) {
IOException canceledException = new IOException("canceled due to " + t);
canceledException.addSuppressed(t);
responseCallback.onFailure(RealCall.this, canceledException);
}
throw t;
} finally {
client.dispatcher().finished(this);
}
}
看看OkHttp创建的具体的线程执行者!
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Util.threadFactory("OkHttpDispatcher", false));
}
return executorService;
}
ThreadPoolExecutor 参数说明
- int corePoolSize: 核心线程数,如果是0的话,空闲一段时间后所有线程将全部被销毁。
- int maximumPoolSize: 最大线程数,当任务进来时可以扩充的线程最大值,当大于了这个值就会根据丢弃处理机制来处理。
- long keepAliveTime: 当线程数大于corePoolSize时,多余的空闲线程的最大存活时间,类似于HTTP中的Keep-alive。
- TimeUnit unit: 时间单位,一般用秒。
- BlockingQueue workQueue: 工作队列,先进先出。
- ThreadFactory threadFactory: 单个线程的工厂,可以打Log,设置Daemon(即当JVM退出时,线程自动结束)。
小结一下:
至此,我们以异步请求为例,弄清楚了 请求去了哪里?
请求怎么在队列中进行维护的?
,那么同步请求也一目了然,直接别放到了runningSyncCalls
队列中,直接执行。
3.2 请求如何被处理?返回如何被接收?
接下来我们看看请求是如何被一步一步处理并发给服务器的,这就不得不分析一波,OkHttp的经典的拦截器责任链。
Response getResponseWithInterceptorChain() throws IOException {
// Build a full stack of interceptors.
List<Interceptor> interceptors = new ArrayList<>();
interceptors.addAll(client.interceptors());
interceptors.add(new RetryAndFollowUpInterceptor(client));
interceptors.add(new BridgeInterceptor(client.cookieJar()));
interceptors.add(new CacheInterceptor(client.internalCache()));
interceptors.add(new ConnectInterceptor(client));
if (!forWebSocket) {
interceptors.addAll(client.networkInterceptors());
}
interceptors.add(new CallServerInterceptor(forWebSocket));
Interceptor.Chain chain = new RealInterceptorChain(interceptors,
transmitter, null, 0,
originalRequest, this,
client.connectTimeoutMillis(),
client.readTimeoutMillis(),
client.writeTimeoutMillis());
boolean calledNoMoreExchanges = false;
try {
Response response = chain.proceed(originalRequest);
if (transmitter.isCanceled()) {
closeQuietly(response);
throw new IOException("Canceled");
}
return response;
} catch (IOException e) {
calledNoMoreExchanges = true;
throw transmitter.noMoreExchanges(e);
} finally {
if (!calledNoMoreExchanges) {
transmitter.noMoreExchanges(null);
}
}
}
- client.interceptors():用户自定义的 Interceptor。
- RetryAndFollowUpInterceptor:负责失败重试以及重定向。
- BridgeInterceptor:负责把用户构造的请求转换为发送给服务器的请求,把服务器返回的响应转换为对用户友好的响应。
- CacheInterceptor:负责读取缓存以及更新缓存。
- ConnectInterceptor:负责与服务器建立连接。
- client.networkInterceptors():用户自定义的网络层 Interceptor
- CallServerInterceptor:负责从服务器读取响应的数据。
从源码中我们大致看出一个请求在经历了几个拦截器的处理后,在CallServerInterceptor拦截阶段发出,并得到了返回,然后又从最底层的拦截器开始,一层层往上包装返回。所以,我们可以看到,每一个拦截器都会向上返回response。
@Override public Response intercept(Chain chain) throws IOException {
// request阶段处理
Request request = chain.request();
...
// 返回response
return response;
}
最后
我们通过几个问题大致理清了OkHttp3在处理请求的过程中,如何在几个队列中处理请求?如何层层封装请求?如何封装返回?
关于里面更详细的一些细节,比如每个拦截器具体做了什么事情,博主后面会继续更新。