OkHttp3 Dispatcher

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiey94/article/details/83089086

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线程池的初步理解


猜你喜欢

转载自blog.csdn.net/xiey94/article/details/83089086