一、Dispatcher类详解
1.线程池ExecutorService
private @Nullable ExecutorService executorService;
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
由上面代码可以得出Dispatcher内部实现了懒加载的无边界线程池
- SynchronousQueue的每个插入操作必须是等待另外一个线程的移除操作,同样任何一个移除操作都等待另外一个线程的插入操作。因此队列内部其实没有任何一个元素,或者说容量为0,只有移除元素才有元素,显然这是一个快速传递元素的方式,也就是说在这种情况下元素总是以最快的方式从插入者(生产者)传递给移除者(消费者),这在多任务队列中最快的处理任务方式。对于高频请求场景,无疑是最合适的。
- 该线程池不保留任何最小线程,随时创建更多的线程,如果线程空闲后,只能存活60秒。所以也就是说如果收到10个请求,线程池会创建10个线程,当完成后60秒会关闭所有10个线程。这样设成不设上限的线程,让I/O任务不会长时间卡在阻塞上。
2.发起请求
整个框架主要通过Call来封装每一次请求。同时Call持有OkHttpClient的Request,每次同步、异步请求都通过Dispatcher来调度
- 同步:Dispatcher在执行同步Call时,并没有做什么处理,只把Call添加到同步请求队列中,具体的操作还是在RealCall中完成的
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
- 异步:首先会把AsyncCall(其实是Runnable)加到等待执行队列中。接着走到promoteAndExecute()方法中,该方法会先遍历等待队列,如果请求数没超过64,并且同一主机请求数不超过5,就会把当前遍历到的AsyncCall从等待队列中移除,再把它加到异步请求队列中。
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
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();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
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;
}
3.结束请求
从准备到运行结束,每个Call都会走到finished方法
private <T> void finished(Deque<T> calls, T call) {
Runnable idleCallback;
synchronized (this) {
// 请求结束,从队列中移除
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
idleCallback = this.idleCallback;
}
// 从等待请求队列中拿AsyncCall,放到请求队列中,从线程池中取线程执行该请求
boolean isRunning = promoteAndExecute();
if (!isRunning && idleCallback != null) {
idleCallback.run();
}
}
promoteAndExecute方法负责ready的Call到running的Call的转化,具体的执行都是在RealCall中,同步是RealCall的execute方法,异步是AsyncCall的execute方法。里面都是调用RealCall的getResponseWithInterceptorChain方法来实现责任链的调用。
二、OkHttp任务调度
1.同步
- 调用RealCall的execute方法里的execute(this)
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
timeout.enter();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
e = timeoutExit(e);
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
- 在Dispatcher的execute方法进行入队操作
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
- 执行getResponseWithInterceptorChain方法,进入拦截器链,然后进行请求,拿到Response并返回
- 任务执行完毕,调用Dispatcher的finished方法,将任务移出队列
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call);
}
2.异步
- 调用RealCall的enqueue方法里的enqueue(new AsyncCall(responseCallback))
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
- 在Dispatcher的enqueue方法进行入队操作,把Call加到等待请求的队列中
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
- promoteAndExecute方法将等待队列中的任务取出来,放到请求队列。前提是要满足条件,请求数不超过64,同一请求的数量不超过5
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();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
i.remove();
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;
}
- 线程池执行execute方法
executorService.execute(this);
- 执行AsyncCall的execute方法
@Override protected void execute() {
boolean signalledCallback = false;
timeout.enter();
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
e = timeoutExit(e);
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
eventListener.callFailed(RealCall.this, e);
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
- 执行getResponseWithInterceptorChain方法,进入拦截器链,然后进行请求,拿到Response
- 如果是正常拿到的response,则调用responseCallback.onResponse方法将响应返回
- 请求执行完毕,调用Dispatcher的finished方法,将请求进行出队处理,再去判断等待队列是否有请求,满足条件就加入请求队列(是一个继续入队的操作)
void finished(AsyncCall call) {
finished(runningAsyncCalls, call);
}
三、OkHttp任务调度优点
- 使用Dispatcher进行任务调度,实现很好的解耦,与线程池配合实现了高并发,低阻塞,提高请求效率
- 请求Call通过Deque作为存储集合,很好地控制先进先出,保证请求顺序