OkHttp 系列 - Dispatcher

426 阅读1分钟

成员变量

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 么。