OkHttp3源码分析(中):请求管理 Dispatcher

Dispatcher封装了管理、处理call的策略,以及实现了线程池。


1. 请求管理策略

1.1 请求队列分类

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<>();
  ...
}

Dispatcher内部维护了3个请求队列,都是双端队列Deque, 可以从执行策略与请求类型两个维度来划分:

1.1.1 按执行策略分类

进行队列:runningAsyncCalls + runningSyncCalls
等待队列:readyAsyncCalls

  // 等待队列中请求数量
  public synchronized int queuedCallsCount() {
    return readyAsyncCalls.size();
  }
  
  // 进行队列中请求数量 (正在处理的:同步请求 + 异步请求)
  public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }

1.1.2 按请求类型分类

异步请求队列:runningAsyncCalls + readyAsyncCalls
同步请求队列:runningSyncCalls

  // 添加到异步请求队列
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }
  
  // 添加到同步请求队列
  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
  1. 异步请求,进入Dispatcher流程,来管理、分发请求,并调用线程池执行请求;
  2. 同步请求,进入Dispatcher流程仅仅是为了计算1.1.1中的runningCallsCount(),
    其执行请求的过程是直接在RealCall#execute()方法中触发的。

1.2.异步请求与同步请求

1.2.1 RealCall发起异步请求

RealCall#enqueue(Callback responseCallback)

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

异步请求,会将call

  1. 添加到异步请求队列中
  2. Dispatcher会根据情况添加到“进行”与“等待”两个队列中,并在线程池中执行请求
  3. (进入线程池)具体执行是RealCall.AsyncCall#execute()方法,内部调用getResponseWithInterceptorChain()返回响应结果Response
  4. 通知Dispatcher,当前请求finish

Dispatcher#enqueue

  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

Dispatcher#enqueue流程,管理、分发请求,触发线程池执行请求。

1.2.2 RealCall发起同步请求

RealCall#execute()

  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    try { 
      // 添加到同步请求队列runningSyncCalls
      client.dispatcher().executed(this);
      // 当前线程执行请求
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

同步请求是直接在RealCall#execute()方法中执行的,运行在当前线程(发起调用的线程),没有callback,直接返回响应结果。
getResponseWithInterceptorChain()方法的具体实现,在下一篇文章中讲解。

  1. 添加到同步请求队列
  2. 直接调用getResponseWithInterceptorChain()返回响应结果
  3. 调用Dispatcher#finish, 触发下一个请求

Dispatcher#executed

  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

2. 线程池定义

  // 懒加载:用时才创建
  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;
  }

线程池仅仅对异步请求有作用,所以采用懒加载的方式创建线程池。
在仅有同步请求的情况下,避免创建无用对象。

3.总结

  1. 管理请求队列
    维护3个Deque队列,管理同步、异步请求(ArrayDeque内部使用循环数组来实现Stack和Queue的功能,效率高,推荐使用,非线程安全)
  2. 分发请求
    异步请求是RealCall交给Dispatcher管理,在Dispatcher#enqueue() 方法中发起,提交给线程池执行;
    同步请求直接在RealCall#execute() 方法中发起,当前线程中执行。
  3. 线程池
    线程池仅仅针对异步请求有作用,注意区分Dispatcher对两种请求的不同作用。
    异步请求:Dispatcher来管理队列、分发请求到线程池执行;
    同步请求:Dispatcher仅仅是管理队列,分发请求的过程在RealCall中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值