转载

揭秘网络框架第二篇: OkHttp 请求原理基本认识

    • OkHttp 请求原理基本认识

我们开始学习 OkHttp 都具体帮我们做了哪些操作,大概会分三小节来学习它:

  1. OkHttp 请求实现流程基本认识
  2. OkHttp 核心机制深入学习
  3. OkHttp 请求实现流程总结

OkHttp 请求原理基本认识

OkHttp 的使用比较简单,发起一个异步请求的代码如下:

private void testOkHttp() {

        OkHttpClient okHttpClient = getOkHttpClient();    //构造 OkHttpClient
        Request request = new Request.Builder()
                .get()  //Method GET
                .url("www.baidu.com")
                .build();    //构造请求信息

        okHttpClient.newCall(request)
                .enqueue(new Callback() {    //发起异步请求
                    @Override
                    public void onResponse(final Call call, final Response response) throws IOException {
                        //成功拿到响应
                        int code = response.code();
                        ResponseBody body = response.body();
                        String string = body.string();
                    }

                    @Override
                    public void onFailure(final Call call, final IOException e) {
                        e.printStackTrace();
                    }
                });
    }

可以看到,使用 OkHttp 发起一个异步请求主要三步:

  1. 需要构造一个 OkHttpClient
  2. 构造请求信息 Request
  3. 发起请求

那么在这简单的代码背后, OkHttp 都做了什么呢?请求是如何被执行的呢?响应是如何拿到的呢?

我们先看一下 请求的提交流程

请求的提交流程

我们查看一下 okHttpClient.newCall(request) 方法的源码:

@Override public Call newCall(Request request) {
  return RealCall.newRealCall(this, request, false /* for web socket */);
}

newCall(Request) 方法调用了 RealCall.newRealCall() 方法:

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
  RealCall call = new RealCall(client, originalRequest, forWebSocket);
  call.eventListener = client.eventListenerFactory().create(call);
  return call;
}

这个 RealCall.newRealCall()` 方法创建了一个新的 RealCall 对象,这个 RealCall okhttp3.Call`` 接口的一个实现。

okhttp3.Call 表示一个等待执行的请求,它只能被执行一次,定义了这些方法:

public interface Call extends Cloneable {
  //返回这个请求关联的 Request 对象
  Request request();

  //立即执行请求,阻塞等待拿到响应
  Response execute() throws IOException;

  //请求入队,异步执行
  void enqueue(Callback responseCallback);

  //取消一个请求
  void cancel();

  boolean isExecuted();

  boolean isCanceled();

  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

可以看到,我们前面发起异步请求的 enqueue() 方法是定义在 Call 中的。

okHttpClient.newCall(request)
                .enqueue(new Callback() { ...});        //原来就是 Call 的方法

在 OkHttp 中, Call 的唯一实现就是 RealCall ,它表示一个准备好被执行的请求。和 Request 不同在于,它还提供了发起请求、取消等方法。

在 Retrofit 中也定义了一个 Call 接口,不过它俩层次不一样,相较于 OkHttp.CallRetrofit.Call 增加了更多信息,这个我们后面介绍。

拿到 OkHttp.Call 的实例、 RealCall 对象后,我们调用了它的 enqueue() 方法:

//RealCall.enqueue()
@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));
}

核心就在最后这句 client.dispatcher().enqueue(new AsyncCall(responseCallback)); ,它做了两件事:

  1. 创建一个 AsyncCall 对象
  2. 调用 Dispatcher.enqueue() 方法将请求入队

先看下 AsyncCall 是何方神圣:

final class AsyncCall extends NamedRunnable {
  private final Callback responseCallback;

  AsyncCall(Callback responseCallback) {
    super("OkHttp %s", redactedUrl());
    this.responseCallback = responseCallback;
  }

  String host() {    //用于标识这个请求
    return originalRequest.url().host();
  }

  Request request() {
    return originalRequest;
  }

  RealCall get() {
    return RealCall.this;
  }

  @Override protected void execute() {
    //...
  }
}

可以看到, AsyncCall 就是一个 Runnable,用于异步执行任务。

接着看 client.dispatcher() 方法,它返回一个调度器 Dispatcher ,这是 OkHttp 中比较核心的一个类:

public final class Dispatcher {
  private int maxRequests = 64;    //同时最多发起 64 个请求
  private int maxRequestsPerHost = 5;    //同一 host 最多发起 5 个请求
  private @Nullable Runnable idleCallback;

  private @Nullable ExecutorService executorService;    //将会异步创建的线程池

  //等待被执行的异步请求队列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  //正在运行的异步请求队列(其中也包括取消后没有完成的请求)
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  //正在运行的同步请求队列
  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;
  }
    //...
}

我们可以得到比较关键的信息如下:

  • 在一个 OkHttpClient 中一般只有一个 Dispatcher ,因此一个 OkHttpClient 能发起的最多请求就是 Dispatcher 中定义的 64 个
  • 同样,同一 host 能发起的最多请求是 5 个
  • Dispatcher 中用三个队列保存同步、异步请求
  • 默认线程池核心线程数量为 0,最多数量不限制,消息队列为 SynchronousQueue ,因此有请求时会不断创建新线程

然后回到我们之前调用的 Dispatcher.enqueue() 方法:

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

可以看到,调度器在收到一个异步请求后,会先判断当前正在运行的异步请求是否超过默认的 64 个、同一 host 的请求是否小于默认的 5,是的话就开始执行;否则加入等待执行的队列中。

前面我们介绍了 AsyncCall 是一个 NamedRunnable ,等它被执行时会调用它的 run() 方法,这个方法调用了 execute() 方法。

public abstract class NamedRunnable implements Runnable {
  //...
  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

到这里我们了解了 一个请求从提交到执行背后所经历的 ,用一张图小结一下:

揭秘网络框架第二篇: OkHttp 请求原理基本认识

一个异步请求在发起到执行要经历这么几个状态:

原文  https://xiaozhuanlan.com/topic/6745138902
正文到此结束
Loading...