OkHttp3 Dispatcher
调度器
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private @Nullable Runnable idleCallback;
/** Executes calls. Created lazily. */
private @Nullable ExecutorService executorService;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
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;
}
/**
* Set the maximum number of requests to execute concurrently. Above this requests queue in
* memory, waiting for the running calls to complete.
*
* <p>If more than {@code maxRequests} requests are in flight when this is invoked, those requests
* will remain in flight.
*/
public synchronized void setMaxRequests(int maxRequests) {
if (maxRequests < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequests);
}
this.maxRequests = maxRequests;
promoteCalls();
}
public synchronized int getMaxRequests() {
return maxRequests;
}
/**
* Set the maximum number of requests for each host to execute concurrently. This limits requests
* by the URL's host name. Note that concurrent requests to a single IP address may still exceed
* this limit: multiple hostnames may share an IP address or be routed through the same HTTP
* proxy.
*
* <p>If more than {@code maxRequestsPerHost} requests are in flight when this is invoked, those
* requests will remain in flight.
*
* <p>WebSocket connections to hosts <b>do not</b> count against this limit.
*/
public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
if (maxRequestsPerHost < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
}
this.maxRequestsPerHost = maxRequestsPerHost;
promoteCalls();
}
public synchronized int getMaxRequestsPerHost() {
return maxRequestsPerHost;
}
/**
* Set a callback to be invoked each time the dispatcher becomes idle (when the number of running
* calls returns to zero).
*
* <p>Note: The time at which a {@linkplain Call call} is considered idle is different depending
* on whether it was run {@linkplain Call#enqueue(Callback) asynchronously} or
* {@linkplain Call#execute() synchronously}. Asynchronous calls become idle after the
* {@link Callback#onResponse onResponse} or {@link Callback#onFailure onFailure} callback has
* returned. Synchronous calls become idle once {@link Call#execute() execute()} returns. This
* means that if you are doing synchronous calls the network layer will not truly be idle until
* every returned {@link Response} has been closed.
*/
public synchronized void setIdleCallback(@Nullable Runnable idleCallback) {
this.idleCallback = idleCallback;
}
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
/**
* Cancel all calls currently enqueued or executing. Includes calls executed both {@linkplain
* Call#execute() synchronously} and {@linkplain Call#enqueue asynchronously}.
*/
public synchronized void cancelAll() {
for (AsyncCall call : readyAsyncCalls) {
call.get().cancel();
}
for (AsyncCall call : runningAsyncCalls) {
call.get().cancel();
}
for (RealCall call : runningSyncCalls) {
call.cancel();
}
}
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
/** Returns the number of running calls that share a host with {@code call}. */
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.get().forWebSocket) continue;
if (c.host().equals(call.host())) result++;
}
return result;
}
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
/** Returns a snapshot of the calls currently awaiting execution. */
public synchronized List<Call> queuedCalls() {
List<Call> result = new ArrayList<>();
for (AsyncCall asyncCall : readyAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
/** Returns a snapshot of the calls currently being executed. */
public synchronized List<Call> runningCalls() {
List<Call> result = new ArrayList<>();
result.addAll(runningSyncCalls);
for (AsyncCall asyncCall : runningAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
public synchronized int queuedCallsCount() {
return readyAsyncCalls.size();
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
}
维护了三个请求队列
/** Ready async calls in the order they'll be run. */
//就绪状态的异步请求队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
//运行状态的异步请求队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
//运行状态的同步请求队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
maxRequests:最大请求数;
maxRequestsPerHost:相同host最大请求数
每当有同步请求到Dispatcher中来,就加入到runningSyncCalls中;
每当有异步请求到Dispatcher中来,根据maxRequests和maxRequestsPerHost来判断是加入到runningAsyncCalls中还是加入到readyAsyncCalls;如果加入到就绪队列中则等运行队列有空间了,就加入到运行队列中;
看一下里面所有的参数和方法:
变量/方法 | 解释 |
---|---|
public Dispatcher(ExecutorService executorService) | 构造,传入一个线程池 |
public Dispatcher() | 无参构造 |
public synchronized ExecutorService executorService() | 构建一个ThreadPoolExecutor, 核心线程数是0(默认会一直存活), 最大线程数超大,2的32次方, 闲置超时时间1分钟, 工作队列,添加到线程池都在这, 线程工厂,可创建新线程 |
public synchronized void setMaxRequests(int maxRequests){} | 设置异步最大请求数 |
public synchronized int getMaxRequests() | 获取异步最大请求数 |
public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) | 最大请求主机数 |
public synchronized int getMaxRequestsPerHost() | 获取单个主机最大请求数 |
public synchronized void setIdleCallback(@Nullable Runnable idleCallback) | 设置回调 |
synchronized void enqueue(AsyncCall call) | 执行异步回调 |
public synchronized void cancelAll() | 取消所有请求 |
private void promoteCalls() | 就绪队列转移到工作队列 |
private int runningCallsForHost(AsyncCall call) | 同一主机下的异步线程数 |
synchronized void executed(RealCall call) | 添加到同步工作队列 |
void finished(AsyncCall call) | 结束异步工作Call |
void finished(RealCall call) | 结束同步工作Call |
private void finished(Deque calls, T call, boolean promoteCalls) | 结束运行的Call |
public synchronized List queuedCalls() | 就绪Call回调 |
public synchronized List runningCalls() | 工作Call回调 |
public synchronized int queuedCallsCount() | 就绪线程数 |
public synchronized int runningCallsCount() | 工作线程数 |
private int maxRequests = 64; | 最大请求数 |
private int maxRequestsPerHost = 5; | 最大请求主机数 |
private @Nullable Runnable idleCallback; | 回调 |
private @Nullable ExecutorService executorService; | 线程池 |
private final Deque readyAsyncCalls = new ArrayDeque<>(); | 异步就绪队列 |
private final Deque runningAsyncCalls = new ArrayDeque<>(); | 异步工作队列 |
private final Deque runningSyncCalls = new ArrayDeque<>(); | 同步工作队列 |
就一个一个来看:
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
传入一个线程池对象;
public Dispatcher() {
}
空构造;
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;
}
获取线程池对象 ,如果为空就创建一个线程池;看看里面的参数,核心线程没有,最大线程数量蛮大的,Integer.MAX_VALUE,闲置时间是1分钟;
public synchronized void setMaxRequests(int maxRequests) {
if (maxRequests < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequests);
}
this.maxRequests = maxRequests;
promoteCalls();
}
设置最大请求数;小于1就是挑事儿,没得谈;否则就去执行promoteCalls();
呵呵,自己跳出来了;
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
异步工作队列里面已经超出最大限制了,或者异步就绪队列空了,都不管;否则就去迭代一部就绪队列,拿到每一个AsyncCall;
又跳出来一个方法runningCallsForHost;
private int runningCallsForHost(AsyncCall call) {
int result = 0;
for (AsyncCall c : runningAsyncCalls) {
if (c.get().forWebSocket) continue;
if (c.host().equals(call.host())) result++;
}
return result;
}
这个方法就是根据传进来的Call,判断所属的host在异步工作队列中的Call还有多少(就是说:异步工作队列中,和传进来的Call属于同一个host的有多少个?)
回到上一个方法的出发点,如果统计的结果数没有超过单个host允许的最大数量就从就绪队列中移除它并添加到异步工作队列中,然后扔到线程池中去执行;遗留问题2 如果这样添加一直到异步工作队列到了阈值,就不再添加;
总的来说这一块的代码就是说:设置最大请求阈值,如果新设置的阈值比以前小,这事儿咱就算过了;如果比以前的值大,那就有搞头,把就绪队列中Call往异步工作队列中塞,直到到达阈值结束;
再往下走:
public synchronized int getMaxRequests() {
return maxRequests;
}
获取请求最大阈值;
public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
if (maxRequestsPerHost < 1) {
throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
}
this.maxRequestsPerHost = maxRequestsPerHost;
promoteCalls();
}
单个host最多允许多少个call;同样,小于1没得谈,同样调用promoteCalls,作用和之前的差不多,如果往大了调了,且异步工作队列有空了,就可以网工作队列里面塞,然后归拢到对应的host;
public synchronized int getMaxRequestsPerHost() {
return maxRequestsPerHost;
}
获取单个host允许最多的请求个数;
public synchronized void setIdleCallback(@Nullable Runnable idleCallback) {
this.idleCallback = idleCallback;
}
设置回调,这是个什么回调呢?一会儿说遗留问题1
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
看名字就知道是要执行一个Call,如果异步工作线程小于阈值,并且当前Call所属的host中的Call也小于阈值,那就把它添加到工作线程中,同时扔到线程池中;要是两个阈值都没开放,那就先进入异步就绪队列中。
public synchronized void cancelAll() {
for (AsyncCall call : readyAsyncCalls) {
call.get().cancel();
}
for (AsyncCall call : runningAsyncCalls) {
call.get().cancel();
}
for (RealCall call : runningSyncCalls) {
call.cancel();
}
}
看看名字cancelAll,取消所有;就是取消所有的Call,里面的内容也看的简单,遍历三个队列,然后逐个取消;有个顺序,先干掉就绪的,然后干掉异步的,最后干掉同步的;
再往里看看,cancel,cancel啥呢,点进去,是RetryAndFollowUpInterceptor,看名字,这是一个拦截器啊;好就到这,不深入了;遗留问题4
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
添加到同步队列中来,和那个添加到异步队列中比较一下
方法 | 参数 |
---|---|
enqueue(AsyncCall call) | AsyncCall |
executed(RealCall call) | RealCall |
第一样看过去,除了方法名不一样,就是参数不一样了,异步的是AsynCall,同步的是RealCall;源码追踪一下,可以看出AsynCall是RealCall的内部final类;遗留问题5
再往下看:
/** Used by {@code AsyncCall#run} to signal completion. */
void finished(AsyncCall call) {
finished(runningAsyncCalls, call, true);
}
/** Used by {@code Call#execute} to signal completion. */
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
int runningCallsCount;
Runnable idleCallback;
synchronized (this) {
if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
if (promoteCalls) promoteCalls();
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
是3个finish;都是头两个调用第三个,看参数可以知道,第一个是针对异步的,第二个是针对同步的;把运行中的Call扔进来,然后从队列中移除它,如果是AsycCall,那就要去搬运了(从就绪队列中搬运到工作队列中,有位置空出来就要补上);统计当前的运行Call(异步+同步)的个数,如果当前处于运行状态的没有并且回调不是空,则回调跑起来;
回调中到底跑了啥?回头再说;遗留问题1
/** Returns a snapshot of the calls currently awaiting execution. */
public synchronized List<Call> queuedCalls() {
List<Call> result = new ArrayList<>();
for (AsyncCall asyncCall : readyAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
将当前的异步就绪队列扔到一个List中,然后Collections.unmodifiableList(result)了一下,这玩意是干啥的?
java.util.Collections.unmodifiableList()方法实例
就是这个返回的List是不可修改了;那这个是用于什么的呢?呵呵,等会再说;遗留问题3
/** Returns a snapshot of the calls currently being executed. */
public synchronized List<Call> runningCalls() {
List<Call> result = new ArrayList<>();
result.addAll(runningSyncCalls);
for (AsyncCall asyncCall : runningAsyncCalls) {
result.add(asyncCall.get());
}
return Collections.unmodifiableList(result);
}
把所有的同步Call添加到List中,然后把所有的异步工作队列循环添加进来;最后同样操作,返回不可修改的List;同样等会再说他是干啥的?
public synchronized int queuedCallsCount() {
return readyAsyncCalls.size();
}
public synchronized int runningCallsCount() {
return runningAsyncCalls.size() + runningSyncCalls.size();
}
两个都是统计Call数量的,一个是统计就绪数量,一个是统计工作数量;
方法到这就解释完了,再来看看变量;
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
两个阈值,最大请求数默认64个,这个是针对异步工作线程的;单个host对应的阈值默认是5个,一个host一次最多就要负责五个Call;
private @Nullable Runnable idleCallback;
看到这个是不是恨得牙痒痒,好几次碰到都被我打发到以后再说,其实我也不知道他是干嘛的,只是想等分析完再去看看他是干啥的;老规矩,等会再说;
private @Nullable ExecutorService executorService;
线程池;
java线程池的初步理解
里面四个开辟线程池的方法,内部就是调用的ThreadPoolExecutor;
/** Ready async calls in the order they'll be run. */
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
这三个队列开头就讲过;
这样,该类中所有的方法和变量都撸了一遍,再来看看我们遗留了多少问题;
1、那个idleCallback回调是怎么回事?在哪里回调?回调时做了什么操作?
2、线程中到底是怎么处理那些Call的?
3、统计了就绪队列的数量和工作队列的数量,结果返回的List不可修改,为什么要这么做?用在了什么地方?
4、cancelAll中我们碰到了拦截器,这个当先我现在不做解释,在以后到拦截器的时候再说;
5、还有AsycCall和RealCall的异同,我们只是追随到AsycCall是RealCall的内部final类
除了这些问题我们都能看懂代码逻辑;
五个问题且听下回分解;
参考:
OkHttp3 源码解读
One Step By One Step 解析OkHttp3 - Dispatcher (一)
java.util.Collections.unmodifiableList()方法实例
java线程池的初步理解