什么是dispatcher?
dispatcher的作用是维护同步和异步的请求状态,并且内部维护一个线程池,用于执行相应的请求。
okHttp如何实现同步异步请求?
发送的同步/异步请求都会在dispatcher中管理其状态
接下来看一下dispatcher源码
看一下同步请求dispather.execute()是如何实现的
synchronized void executed(RealCall call) {
this.runningSyncCalls.add(call);
}
直接的会把当前的请求放到同步队列当中,并没有向异步请求有限制
接下来看一下异步请求是如何实现的?
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
synchronized void enqueue(AsyncCall call) {
if (this.runningAsyncCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) {
this.runningAsyncCalls.add(call);
this.executorService().execute(call);
} else {
this.readyAsyncCalls.add(call);
}
}
当前正在运行的异步数量是否超过64或者相同主机请求数量是否超过5,如果满足的话放到正在执行的异步队列中,并且交给线程池去处理这个请求,否则放到就绪队列当中去,进行缓存等待的操作
public synchronized ExecutorService executorService() {
if (this.executorService == null) {
this.executorService = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
}
return this.executorService;
}
ThreadPoolExecutor方法第一个参数设置为0有什么意义呢?其实是为了销毁全部线程,第二个参数表示最大的线程数,理论上可以无限扩大线程,但是由于OKHttp请求限制,也就不能无限的创建线程,第三个参数,当我们的线程数大于我们的核心线程数的时候,多出来的最多存活时间也就是60秒
Call执行完肯定需要在runningAsyncCalls队列中移除这个线程。那么readyAsyncCalls队列中的线程在什么时候才会被执行呢?
由上一篇文章知道,异步请求的运行都是在RealCall中内部类AsyncCall中执行的Run方法,而execute()就是实现run方法的地方,接下来看一下代码
protected void execute() {
boolean signalledCallback = false;
try {
Response response = RealCall.this.getResponseWithInterceptorChain();
if (RealCall.this.retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
this.responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
this.responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException var6) {
if (signalledCallback) {
Platform.get().log(4, "Callback failure for " + RealCall.this.toLoggableString(), var6);
} else {
this.responseCallback.onFailure(RealCall.this, var6);
}
} finally {
RealCall.this.client.dispatcher().finished(this); //不管到底有木有异常都会执行finished这个方法,前面的方法都是对respose成功失败的回调
}
}
接下来看一finish到底都做了一些什么操作
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) {//调整我们的任务队列,不管是同步还是异步请求当前的线程都是不安全的,所以加了一个同步锁给锁住
this.promoteCalls();//①这个方法到底做了什么?
}
runningCallsCount = this.runningCallsCount();//重新计算了目前正在执行的线程数量
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
接下来看一下promoteCalls()都干了什么?
private void promoteCalls() {
if (this.runningAsyncCalls.size() < this.maxRequests) {//判断当前的正在执行的异步任务数量是否小于最大数量
if (!this.readyAsyncCalls.isEmpty()) {//如果正在就绪等待的异步任务数量不是空的
Iterator i = this.readyAsyncCalls.iterator(); //就会遍历这个就绪等待的异步请求队列
do {
if (!i.hasNext()) {
return;
}
AsyncCall call = (AsyncCall)i.next();
if (this.runningCallsForHost(call) < this.maxRequestsPerHost) {
i.remove();//它会把这个就绪等待的异步请求删除
this.runningAsyncCalls.add(call);//然后添加到正在执行的异步队列当中
this.executorService().execute(call);//然后通过线程池去执行这个请求
}
} while(this.runningAsyncCalls.size() < this.maxRequests);
}
}
}
总结:每当有新的异步请求到Dispatcher这个管理类来的时候,通过调用call.enqueue(),通过判断,当前正在运行的异步数量是否超过64或者相同主机请求数量是否超过5,如果没有超出就会正常放到正在运行的异步队列当中去执行,如果超出数量的话,那么就会把当前请求放到就绪的队列当中去。等运行队列有空间了,再把就绪队列中的异步请求,放到正在执行的异步队列中来执行,当任务执行完成,它会调用finish中的promoteCall()调度任务的方法,然后判断当前正在执行的异步队列当中是否还有位置,有的话就会把就绪等待的异步任务放到正在执行的异步队列中,然后通过线程池去执行。
接下来会继续研究一下OKHttp的拦截器是如何操作的