成员变量
public final class Dispatcher {
// 管理调度的策略
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
// ...
// 线程池
private @Nullable ExecutorService executorService;
// 存放 待执行 的异步 call 的队列(存放 runnable 的队列)
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
// 存放 正在执行 的异步 call 的队列(存放 runnable 的队列)
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
// 存放正在运行的同步 call 的队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
//...
}
通过观察成员变量,我们来推测一下 Dispatcher 都做了些什么。
首先看到的就是 maxRequests 和 maxRequestsPerHost,他们的命名都是 max什么什么,是用来作为某些筛选的条件的。通过命名含义看出,maxRequests 是请求的最大数量,maxRequestsPerHost 是对同一个 host 的最大请求数量
其次,看数据结构,它维护了三个队列:runningSyncCalls、runningAsyncCalls、readyAsyncCalls,分别存放着运行中的同步请求,运行中的异步请求任务,准备运行的异步请求任务。
最后,看到它有一个线程池,推测它是用来执行异步请求的 Runnable 的。
关键源码
// -- RealCall.java
@Override public void enqueue(Callback responseCallback) {
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
// -- Dispatcher.java
void enqueue(AsyncCall call) {
synchronized (this) {
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
private boolean promoteAndExecute() {
List<AsyncCall> executableCalls = new ArrayList<>();
// 筛选出 readyAsyncCalls 里符合要求的 asyncCall, 加入到 runningAsyncCalls 队列中
synchronized (this) {
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.
//...
i.remove();
//...
runningAsyncCalls.add(asyncCall);
}
// 筛选出的 asyncCall 交给线程池执行
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
//...
}
上面是 Dispatcher 类的关于异步网络任务的关键方法和源码,同步任务的逻辑大同小异,这里就不说了。
上面代码的大致意思是,把一个异步网络请求 asyncCall 被放入了等待队列,然后 promoteAndExecute 方法根据 maxRequests 和 maxRequestsPerHost 来筛选出在等待队列中的异步任务,交给线程池执行,其实这也就是 Dispatcher 的核心功能:通过三个队列来管理 call,然后交给线程池执行,剩下的就 Dispatcher 啥事儿了。
作用
没有 Dispatcher 会发生什么呢?假设现在有个异步网络请求要执行,现在没了 Dispatcher,那就直接丢给线程池吧,好像没啥问题,那如果现在突然来大活了,有 9998 个异步网络请求和 9998 个同步网络请求要立即执行,那完了个蛋了,异步任务直接把线程池挤爆,同步任务总需要个地方保存他们来一个一个执行吧,自然想到要弄个队列来保存他们,这不就是 Dispatcher 么。