Square开源网络请求库OkHttp的工作原理解析

前言

说句废话,作为一个工作几年的程序员,在日常工作中,难免会用一些三方封装库,来方便我们的开发,但是不能仅仅会用就满足,我们还了解它的工作原理。

正文

转入正题,看到本文的朋友应该知道了OK给出的API的基本调用(本文不对基本使用做介绍,有需要自行看https://github.com/square/okhttp/wiki/Recipes),
那下面试着揭开OK的面纱,查看内部是如何实现的。
准备工作: 首先去下载https://github.com/square/okhttp源码,然后maven编译完成后导入IDEA中。

在分析工作流程前,需要先了解几个类:OkHttpClient,Call,Request,Response。

OkHttpClient: Call的工厂,可以发送HTTP请求和读取响应,推荐多个请求共享同一个client,每一个 client都持有自己的connection pool(连接池)和(线程池),reuse 连接和线程,减少了延迟和节省了内存的开支;相反,对每一个Request创建一个client浪费空闲池中的资源。

推荐创建singleton HTTP client的方式
使用:
public final OkHttpClient client = new OkHttpClient();
或者:
public final OkHttpClient client = new OkHttpClient.Builder()
   .addInterceptor(new HttpLoggingInterceptor())
   .cache(new Cache(cacheDir, cacheSize))
   .build();

线程和连接在空闲时会自动释放。
关闭 dispatcher's executor service ,This will also cause future calls to the client to be rejected
client.dispatcher().executorService().shutdown(): 主动释放后,以后的Call 调用也会被拒绝。

Clear the connection pool with client.connectionPool().evictAll() Note that the
清除连接池的操作:client.connectionPool().evictAll(),connection pool's daemon thread may not exit immediately.
连接池的守护线程可能不会立即退出。

关闭客户端缓存的方法

如果client 有cache,调用client.cache().close(),注:创建Calls 使用cache,但是设置了关闭cache会导致报错?
If your client has a cache, call {@link Cache#close close()}. Note that it is an error to
create calls against a cache that is closed, and doing so will cause the call to crash.


Call :A call is a request that has been prepared for execution. 准备被执行的请求,可以取消,一个call实例表示一个请求/响应对(流),只能执行一次。

Request:An HTTP request.

Response:An HTTP response 这个类的实例不是不可变的,ResponseBody(响应体)是一个一次性的值,只被使用一次,然后被关闭。所有其他属性都是不可变的。

接下来,就拿下面这个Get请求为例,看下Ok的内部工作流程吧。

package okhttp3.guide;

import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class GetExample {
  OkHttpClient client = new OkHttpClient();

  String run(String url) throws IOException {
    Request request = new Request.Builder()
        .url(url)
        .build();

    try (Response response = client.newCall(request).execute()) {
      return response.body().string();
    }
  }

  public static void main(String[] args) throws IOException {
    GetExample example = new GetExample();
    String response = example.run("https://raw.github.com/square/okhttp/master/README.md");
    System.out.println(response);
  }
}
流程描述:初始化OkHttpClient 对象,接下来执行run()方法,里面对Request对象进行初始化,然后client.newCall(request).execute()发起请求,返回结果Response。

第一步OkHttpClient初始化

首先对OkHttpClient初始化操作,发起Request前进行默认初始化设置,相当于准备工作。
下面是OkHttpClient 类中的部分属性。

  final Dispatcher dispatcher;
  final @Nullable Proxy proxy;
  final List<Protocol> protocols;
  final List<ConnectionSpec> connectionSpecs;
  final List<Interceptor> interceptors;
  final List<Interceptor> networkInterceptors;
  final EventListener.Factory eventListenerFactory;
  final ProxySelector proxySelector;
  final CookieJar cookieJar;
  final @Nullable Cache cache;
  final @Nullable InternalCache internalCache;
  final SocketFactory socketFactory;
  final @Nullable SSLSocketFactory sslSocketFactory;
  final @Nullable CertificateChainCleaner certificateChainCleaner;
  final HostnameVerifier hostnameVerifier;
  final CertificatePinner certificatePinner;
  final Authenticator proxyAuthenticator;
  final Authenticator authenticator;
  final ConnectionPool connectionPool;
  final Dns dns;
  final boolean followSslRedirects;
  final boolean followRedirects;
  final boolean retryOnConnectionFailure;
  final int connectTimeout;
  final int readTimeout;
  final int writeTimeout;
  final int pingInterval;

第二步 构建Request

接下来构建Request,指定是设置Request的 url ,请求类型 such as get/post …
以及request headers or request body , tag 。

下面是Request类中定义的部分属性。

/**
 * An HTTP request. Instances of this class are immutable if their {@link #body} is null or itself
 * immutable.
 */
public final class Request {
  final HttpUrl url;
  final String method;
  final Headers headers;
  final @Nullable RequestBody body;
  final Object tag;
  private volatile CacheControl cacheControl; // Lazily initialized.

第三步发起Request返回Response

下面是发起请求的代码,我们拆开来看

try (Response response = client.newCall(request).execute()) {
      return response.body().string();
    }

client.newCall(request) 执行完后返回了Call 对象。

  /**
   * Prepares the {@code request} to be executed at some point in the future.
   */
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

我们可以看出RealCall.newRealCall(),返回了Call对象,看下RealCall

final class RealCall implements Call {
	/**省略部分代码*/
  static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }
  
 @Override public Response execute() throws IOException {
   /**省略部分代码*/
    try {
      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 实现了 Call接口,静态方法newRealCall初始化一个RealCall对象,并且有一个execute()方法,这个方法返回值就是Response 对象,也就是说,我们调用client.newCall(request).execute()发起Request,然后返回 Response。

 Response response = getResponseWithInterceptorChain();

RealCall的execute()方法中有这样一行代码,很显然,是getResponseWithInterceptorChain()拿到了对应的Response 信息,一起看下RealCall中的这个方法

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

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());
    return chain.proceed(originalRequest);
  }

getResponseWithInterceptorChain方法首先把所有的interceptors封装到一个集合中,然后把这个集合作为参数,初始化RealInterceptorChain【RealInterceptorChain是拦截器链,承载所有的拦截器链,包含:所有的自定义拦截器,OK内核,所有的网络拦截器,及网络调用。】
最后执行proceed(),现在我们一步步靠近Ok请求的核心了,看下proceed方法

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {
	/**省略部分代码*/
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
	 //=============关键代码==============
    Response response = interceptor.intercept(next);
    /**省略部分代码*/
    return response;
  }

可以看到 chain内部的proceed方法最终还是执行的interceptor.intercept()方法拿到了Response,那几回过头看下前面getResponseWithInterceptorChain()方法中都有那些interceptor

 //只看interceptor
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    //client我们自己设置的interceptor
    interceptors.addAll(client.interceptors());
    //retryAndFollowUpInterceptor:请求失败时,重试和重定向拦截器。
    interceptors.add(retryAndFollowUpInterceptor);
    //从应用程序代码到网络代码的桥梁
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //缓存拦截器 从cache中读取response和写response到 cache
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //打开到目标服务器的连接,并继续执行下一个拦截器
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    //拦截器链中最后一个interceptor,It makes a network call to the server
    interceptors.add(new CallServerInterceptor(forWebSocket));
    /**省略部分代码*/
  }

注释很明显,最后一个CallServerInterceptor拦截器的任务是向Remote Server发起Request。又前面我们知道 调用的都是Interceptor的 intercept()方法,那最终,我们跟踪一个发起的Request到了 CallServerInterceptor的intercept方法中。

@Override public Response intercept(Chain chain) throws IOException {
    /**
     * * A concrete interceptor chain that carries the entire interceptor chain: all application
     * interceptors, the OkHttp core, all network interceptors, and finally the network caller.
     * 拦截器 链 ,ok的核心,拦截器是网咯的调用者
     */
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    /**
     * Encodes HTTP requests and decodes HTTP responses. 编码http请求,解码http响应
     */
    HttpCodec httpCodec = realChain.httpStream();
    /**
     * 协调 Connections 、Stream、 Calls 之间关系的类。
     * Connections: physical socket connections to remote servers  :物理Socket 连接远程服务器
     * Streams:HTTP request/response pairs  HTTP 请求/响应对。
     * Calls:a logical sequence of streams, typically an initial request and its follow up requests.
     *  一系列的流,通常是初始请求及后续请求,推荐每个call上面所有的流,使用相同的链接。
     * We prefer to keep all streams of a single call on the same connection for better behavior and locality.
     */
    StreamAllocation streamAllocation = realChain.streamAllocation();
    /**
     *  HTTP套接字和流  发送和接收数据
     */
    RealConnection connection = (RealConnection) realChain.connection();
    /**
     * HTTP请求
     */
    Request request = realChain.request();

    long sentRequestMillis = System.currentTimeMillis();
    /**
     * 开始处理请求头
     */
    realChain.eventListener().requestHeadersStart(realChain.call());
    httpCodec.writeRequestHeaders(request);
    realChain.eventListener().requestHeadersEnd(realChain.call(), request);

    Response.Builder responseBuilder = null;
    /**
     * 判断请求方法 和请求body 不为空
     */
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return
      // what we did get (such as a 4xx response) without ever transmitting the request body.
  
      if (responseBuilder == null) {
        /**
         * Write the request body if the "Expect: 100-continue" expectation was met.
         * 如果“期望:100继续”的期望得到满足,请编写请求体。
         */
        realChain.eventListener().requestBodyStart(realChain.call());
        long contentLength = request.body().contentLength();
        CountingSink requestBodyOut =
            new CountingSink(httpCodec.createRequestBody(request, contentLength));
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);

        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
        realChain.eventListener()
            .requestBodyEnd(realChain.call(), requestBodyOut.successfulCount);
      } else if (!connection.isMultiplexed()) {
        // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection
        // from being reused. Otherwise we're still obligated to transmit the request body to
        // leave the connection in a consistent state.
        streamAllocation.noNewStreams();
      }
    }
    httpCodec.finishRequest();

    if (responseBuilder == null) {
      realChain.eventListener().responseHeadersStart(realChain.call());
      responseBuilder = httpCodec.readResponseHeaders(false);
    }
    /**
     * 初始化response对象
     */
    Response response = responseBuilder
        .request(request)
        .handshake(streamAllocation.connection().handshake())
        .sentRequestAtMillis(sentRequestMillis)
        .receivedResponseAtMillis(System.currentTimeMillis())
        .build();
    /**
     * 响应头
     */
    realChain.eventListener()
        .responseHeadersEnd(realChain.call(), response);

    int code = response.code();

    if (forWebSocket && code == 101) {
      /**
       *Connection is upgrading, but we need to ensure interceptors see a non-null response body.
       连接正在升级,但是我们需要确保拦截器看到一个非空响应体
        */
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
      /**
	   * 最终执行到这里将response对象,构建完成,然后返回
       * 构建response 的ResponseBody
       */
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();

    }
	//===========删除部分代码========================
 
    return response;
  }

这个方法比较重要,所以要看仔细一点,由此,我们可以得到我们想要的结果了。

结论

一个Request的发起到接收Response 经历的过程:
首先构建OkHttpClient 对象,接下来使用构建好的Request作为参数传递给OkHttpClient 的newCall() 生成RealCall 对象,然后执行call的execute方法,最终这个方法内部通过CallServerInterceptor返回了Response,Response 封装了服务器返回的数据。

大体流程就是这样,更多细节与流程图稍后补充。

欢迎爱学习的小伙伴加群一起进步:230274309 。 一起分享,一起进步!少划水,多晒干货!!欢迎大家!!!(进群潜水者勿加)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值