okHttp3的原理剖析

okHttp3的原理剖析

1.基本使用

先来一个okHttp3最基本的使用实例

Request request = new Request.Builder()
        .url("https://github.com/")
        .addHeader("content-type","text/html")
        .build();
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
    
    
    @Override
    public void onFailure(okhttp3.Call call, IOException e) {
    
    

    }

    @Override
    public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
    
    

    }
})

Request、Response、Call 基本概念

  1. Request
    每一个Http请求都包含了一个URL;一个请求方式(get/post);一些请求头,比如Content-Type,User-Agent和Cache-Control等等;如果是post的请求方式的话,还需要一个请求体(RequestBody),主要包含一个特定内容类型的数据类的主体部分,具体可看下图
    在这里插入图片描述

  2. Response
    响应是对请求的回复,包含状态码、HTTP头和主体部分。

  3. Call
    OkHttp使用Call抽象出一个满足请求的模型,尽管中间可能会有多个请求或响应。执行Call有两种方式,同步或异步(execute()/enqueue() )

2.跟随源码剖析原理

在这里插入图片描述
如上图所示,这是一次Http请求到响应的流程图

第一步:创建OkHttpClient对象

OkHttpClient okHttpClient = new OkHttpClient.Builder().build();

我们来看看okhttpClient的源码

public Builder() {
    
    
  dispatcher = new Dispatcher();
  protocols = DEFAULT_PROTOCOLS;
  connectionSpecs = DEFAULT_CONNECTION_SPECS;
  proxySelector = ProxySelector.getDefault();
  cookieJar = CookieJar.NO_COOKIES;
  socketFactory = SocketFactory.getDefault();
  hostnameVerifier = OkHostnameVerifier.INSTANCE;
  certificatePinner = CertificatePinner.DEFAULT;
  proxyAuthenticator = Authenticator.NONE;
  authenticator = Authenticator.NONE;
  connectionPool = new ConnectionPool();
  dns = Dns.SYSTEM;
  followSslRedirects = true;
  followRedirects = true;
  retryOnConnectionFailure = true;
  connectTimeout = 10_000;
  readTimeout = 10_000;
  writeTimeout = 10_000;
}

如果我们直接build一个OkhttpClient对象出来的,他会给必须的参数设置默认值,但是如果我们需要手动设置一些参数,根据建造者模式的链式调用,也可以很直接明了的进行设置,这也是okHttp3的优势之一,如下所示

OkHttpClient okHttpClient = new OkHttpClient.Builder()
                                    .addInterceptor()
                                    .writeTimeout(100)
                                    .readTimeout(100)
                                    .connectTimeout(100)
                                    .build();

所以此处代码主要是创建OkHttpClient对象,并对一些参数的初始化

第二步:接下来发起 HTTP 请求

 okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
    
    
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
    
    

            }

            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
    
    

            }
        });

先来看看OkHttpClient的newCall ( )方法

public class OkHttpClient implements Cloneable, Call.Factory {
    
    
.......
 @Override public Call newCall(Request request) {
    
    
    return new RealCall(this, request);
  }
  ........
  }

okHttpClient实现了Call 接口,我们可以来看看这个接口类

/**
 * A call is a request that has been prepared for execution. A call can be canceled. As this object
 * represents a single request/response pair (stream), it cannot be executed twice.
 */
 
public interface Call {
    
    

  Request request();  // 返回发起此调用的原始请求。
  
  Response execute() throws IOException;  //同步发送请求,线程阻塞
  
  void enqueue(Callback responseCallback); //异步放松请求,底层利用线程池
 
  void cancel();  //取消请求。 已经完成的请求无法取消
 
  boolean isExecuted(); // 判断是否被execute或者enqueue
 
  bolean isCanceled();// 判断是否cancel

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

接着回来看Okhttpclient实现自Call.Factory接口的 newCall()方法

public Class OkHttpClient{
    
    
......
  @Override public Call newCall(Request request) {
    
    
    return new RealCall(this, request);
  }
  .......
  }

这里创建了Call接口的实现类Realcall,所以前面发起Http请求时调用的enqueue,实际上调用的是Realcall中所实现的enqueue方法

public Class RealCall{
    
    
  @Override public void enqueue(Callback responseCallback) {
    
    
    enqueue(responseCallback, false);
  }

  void enqueue(Callback responseCallback, boolean forWebSocket) {
    
    
    synchronized (this) {
    
    
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
  }
}

这里先贴出另外一个请求方式execute( )方法

public Class RealCall{
    
    
@Override public Response execute() throws IOException {
    
    
    synchronized (this) {
    
    
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
    
    
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain(false);
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
    
    
      client.dispatcher().finished(this);
    }
  }
  }

上面说过,execute( )和 enqueue( ) 的不同之处在于是否同步,前者是同步的(线程阻塞,也就是说在改方法未执行完返回结果前,程序不会继续往下执行的),后者是异步的(线程不阻塞,程序不需要等待返回结果,直接往下执行,等方法执行完,程序会根据回调来返回结果)。现在我们来看看这两个请求方式的源码剖析,如下图
在这里插入图片描述
如图可以,两个方法的区别在于异步的enqueue通过了Dispatcher类的转换之后实现了异步功能,我们回到源码看enqueue ( )方法,client.dispatcher()其实返回的就是Dispathcer的引用,而这个client其实就是上面提到的okHttpClient(Dispatcher属于okHttpClient的成员变量);在剖析Dispatcher的enqueue( ) 方法之前,我们先来看看简单撸一下Dispatcher这个类,先来看看它关键的成员变量:

public final class Dispatcher {
    
    
private int maxRequests = 64;   //最大的并发请求数
  private int maxRequestsPerHost = 5; //每个主机最大的请求数

  /** Executes calls. Created lazily. */
  private 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中的线程池的创建

  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;
  }

由上可知,此线程池是单例的,这里给出线程池构造器ThreadPoolExecutor( ) 里的参数含义:


```java
/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters and default rejected execution handler.
 *
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 * @param maximumPoolSize the maximum number of threads to allow in the
 *        pool
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 * @param unit the time unit for the {@code keepAliveTime} argument
 * @param workQueue the queue to use for holding tasks before they are
 *        executed.  This queue will hold only the {@code Runnable}
 *        tasks submitted by the {@code execute} method.
 * @param threadFactory the factory to use when the executor
 *        creates a new thread
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code threadFactory} is null
 */
public ThreadPoolExecutor(int corePoolSize,      // 最小并发线程数,如果是0,则过一段时间所有线程都将销毁
                          int maximumPoolSize,  // 最大的线程数,当任务进来时,自动扩充的最大线程数,如果超过,则会根据丢弃处理机制来处理 
                          long keepAliveTime,   //当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize

                          TimeUnit unit,        //keepAliveTime的单位
                          BlockingQueue<Runnable> workQueue, //工作队列
                          ThreadFactory threadFactory) {
    
      //线程工厂
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}

大概了解了Dispatcher类,我们在来剖析一下Dispatcher的enqueue( ) 方法

  synchronized void enqueue(AsyncCall call) {
    
    
  //如果正在异步请求的数量小于最大请求数  && 每个主机请求数 小于 主机最大请求数
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
    
    
		// 将请求任务加入异步请求集
      runningAsyncCalls.add(call);
      //执行异步请求
      executorService().execute(call);
    } else {
    
    
      // 将请求任务放入预备异步请求集
      readyAsyncCalls.add(call);
    }
  }

看到这里可能有人会问,runningAsyncCalls和readyAsyncCalls这两个集合有什么用呢?细心的人知道,这个集合是队列,符合先进先出的原理,再来看看enqueue( )的参数AsyncCall:


public class RealCall{
    
    
.....
final class AsyncCall extends NamedRunnable {
    
    
   ......
    @Override protected void execute() {
    
    
      boolean signalledCallback = false;
      try {
    
    
        Response response = getResponseWithInterceptorChain(forWebSocket);  //  1
        if (canceled) {
    
    
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled")); // 2
        } else {
    
    
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response); //3
        }
      } catch (IOException e) {
    
    
        if (signalledCallback) {
    
    
          // Do not signal the callback twice!
          logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
        } else {
    
    
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
    
    
        client.dispatcher().finished(this);  //4
      }
    }
    .........
  }
}

AsyncCall是RealCall的内部类,继承了NameRunable类,

public abstract class NamedRunnable implements Runnable {
    
    
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    
    
    this.name = String.format(format, args);
  }

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

NameRunable实现了Runnable类,到这里相信熟悉线程调用的伙伴们应该很清楚了,线程开始任务后会执行其中的run方法,而NameRunable 的run方法里执行的是execute( )方法,那么自然最终调用的将会是AsyncCall里的具体实现execute方法,我们回过头来继续看AsyncCall的execute 方法;注释1处是获得响应的关键所在,这点待会会重点分析;注释2和注释3,相信大家很熟悉了,就是异步方法的回调,相信大家不知道在那里回调的小伙伴应该清楚了;接着来看注释4,这里就是让任务队列按顺序执行的关键,任务在try/finally中调用了finished函数,控制任务队列的执行顺序,而不是采用锁,减少了编码复杂性提高性能。这里调用的是Dispatcher的finish( )方法,我们来看看:

  synchronized void finished(AsyncCall call) {
    
    
    if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
    promoteCalls();
  }

接着来看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(); ) {
    
     //1
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
    
    
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call); //2
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

这里有用到了runningAsyncCalls和readyAsyncCalls了,所以在每个任务请求结束后,通过调用Dispatcher的finish方法,迭代异步任务请求集(runningAsyncCalls),在注释2处继续执行下一个任务。
相比异步请求,同步请求方式(execute ( ))显得很简单明了了,它并没有重新包装call类(异步请求会调用RealCall里的内部类AsyncCall,里面处理响应以及回调),我们来看看同步方式的代码:

public Class RealCall{
    
    
.....
  @Override public Response execute() throws IOException {
    
    
    synchronized (this) {
    
    
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
    
    
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain(false); //1
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
    
    
      client.dispatcher().finished(this); //2
    }
  }
.....
}

注释1直接得到请求的响应,注释2跟上面一样的道理。
这里粘贴了很多源码,可以小伙伴们会有点乱,这里附上上述的流程图:
在这里插入图片描述

核心重点getResponseWithInterceptorChain方法

在这里插入图片描述

我们在来重点撸一下这个方法。先沾源码:

  private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
    
    
  Response getResponseWithInterceptorChain() throws IOException {
    
    
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());  // 1 
    interceptors.add(retryAndFollowUpInterceptor); // 2
    interceptors.add(new BridgeInterceptor(client.cookieJar()));  // 3
    interceptors.add(new CacheInterceptor(client.internalCache()));  // 4
    interceptors.add(new ConnectInterceptor(client)); // 5
    if (!forWebSocket) {
    
    
      interceptors.addAll(client.networkInterceptors());  // 6
    } 
    interceptors.add(new CallServerInterceptor(forWebSocket)); // 7

    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
    Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
    return chain.proceed(originalRequest);
  }

注释1 :在配置 OkHttpClient 时设置的 interceptors;
注释2:负责失败重试以及重定向的 RetryAndFollowUpInterceptor;
注释3 :负责把用户构造的请求转换为发送到服务器的请求、把服务器返回的响应转换为用户友好的响应的 BridgeInterceptor;
注释4 :负责读取缓存直接返回、更新缓存的 CacheInterceptor;
注释5 :负责和服务器建立连接的 ConnectInterceptor;
注释6 :配置 OkHttpClient 时设置的 networkInterceptors;
注释7:负责向服务器发送请求数据、从服务器读取响应数据的 CallServerInterceptor。

这些拦截器的主要功能如上所述,小伙伴们如果对其感兴趣,可以自行阅读源码,我们重点来理解一下拦截器链的原理,它到底是怎么实现层层拦截的功能的,我们来看一下ApplicationInterceptorChain这个类

class ApplicationInterceptorChain implements Interceptor.Chain {
    
    
    private final int index;
    private final Request request;
    private final boolean forWebSocket;

    ApplicationInterceptorChain(int index, Request request, boolean forWebSocket) {
    
    
      this.index = index;
      this.request = request;
      this.forWebSocket = forWebSocket;
    }

    @Override public Connection connection() {
    
    
      return null;
    }

    @Override public Request request() {
    
    
      return request;
    }

    @Override public Response proceed(Request request) throws IOException {
    
    
      // If there's another interceptor in the chain, call that.
      if (index < client.interceptors().size()) {
    
    
        Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
        Interceptor interceptor = client.interceptors().get(index);
        Response interceptedResponse = interceptor.intercept(chain);

        if (interceptedResponse == null) {
    
    
          throw new NullPointerException("application interceptor " + interceptor
              + " returned null");
        }

        return interceptedResponse;
      }

      // No more interceptors. Do HTTP.
      return getResponse(request, forWebSocket);
    }
  }
public interface Interceptor {
    
    
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    
    
    Request request();

    Response proceed(Request request) throws IOException;

    Connection connection();
  }
}

ApplicationInterceptorChain实现了Interceptor.Chain这个接口

 Interceptor.Chain chain = new ApplicationInterceptorChain(0, originalRequest, forWebSocket);
    return chain.proceed(originalRequest);

在创建ApplicationInterceptorChain实例的时候,我们重点留意一下构造器里的 0 这个参数,其含义是拦截器链的开头;我们在来看看proceed( ) 这个方法;这里首先会判断此时的index是否是小于拦截器集合的大小,如果true,说明此时还有未执行完的拦截器,需要继续给这个链条加长,如源码:

 Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
  Interceptor interceptor = client.interceptors().get(index);
   Response interceptedResponse = interceptor.intercept(chain);

此时index已经加1,并且利用构造器赋值给新的chain;继续往下看,这里用index为索引(注意此时的index还是等于还没有 +1的值,也就是说在这里值还是等于0),获取拦截器集合的第一个拦截器,并且以新创建的chain作为参数执行这个拦截器的intercept( ) 方法,相比自定义过拦截器的小伙伴都知道,拦截器的主要功能实现就是在这个方法中,且最后返回chain,proceed(request),这样就又回到了上述流程,再次走了ApplicationInterceptorChain的proceed( )的方法,只不过此时的index变成了1。其实这种思想就是递归调用的思想。如果index大于拦截器集合的大小的话,则会直接返回响应,源码如下:

 return getResponse(request, forWebSocket);

当拦截器执行完了,这里就开始真正的Http请求了,一般小伙伴们用okHttp3框架原理到这里就可以了,当然感兴趣的小伙伴也可以继续升入探究!

猜你喜欢

转载自blog.csdn.net/qq_39431405/article/details/112231788
今日推荐