okhttp源码流程分析

 

  • 版本说明

本文只考虑okhttp3.0.1版本HTTP1.1协议的源码实现(截止2019-3-20日,github上最新版本为3.14.0),虽然版本不同,但是对本文的主题HTTP1.1部分来说,流程变化不大。

  • Okhttp3.0.1关键类

(1). HttpEngine,http引擎,负责请求发送及接收流程。介绍几个关键的成员对象:client(OkHttpClient),client是从外部传进去的,不能为空,即okhttp调用方创建的OkHttpClient对象,其作用是提供全局的参数或策略配置。streamAllocation(StreamAllocation), 也是从外部传进去的,可以为空,如果为空,在HttpEngine构造函数中会创建streamAllocation。

(2). StreamAllocation,负责生成stream流对象。同样的介绍几个关键成员对象,connectionPool(连接池),连接池在OkHttpClient创建时 生成,然后传入StreamAllocation构造函数。stream对象,即Http1xStream实体类,其在findConnection(创建连接)之后new Http1xStream(StreamAllocation, RealConnection.source, RealConnection.sink) 生成。

(3). ConnectionPool,  负责管理connection连接池,其维护一个连接池队列,连接被允许的最大空闲时间keepAliveDuration为5分钟,当超过keepAliveDuration时,连接会被释放掉;同一时刻允许的最大空闲连接数maxIdleConnections为5个。

(4). RealConnection,负责创建socket;socket建立连接;设置socket参数(读超时时间),注意Socket只提供了读超时时间,所以写超时时间writeTimeOut是如何生效的呢,go on...;生成socket输入流inputStream(数据接收)关联的RealBufferedSource对象;生成socket输出流outputStream(数据发送)关联的RealBufferedSink对象。

(5). Http1xStream,实现数据流的发送和接收。介绍2个关键成员对象,source---(D)中与connection关联的RealBufferedSource对象;sink---(D)中RealBufferedSink对象。

(6). Source接口,Source接口在源码中被实例化的地方在Okio这个类中,Okio借助于Source接口的具体实现对象完成数据接收。Source接口有2个方法,read(Buffer, long)---读操作,将socket 接收到的数据写入Buffer中;timeout()返回Timeout对象。

(7). Sink接口,Sink接口与Source接口相对应,Source完成数据接收,Sink接口则完成数据发送。

(8). RealBufferedSource,内部封装了Source和Buffer两个成员变量。

(9). RealBufferedSink,内部封装了Sink和Buffer两个成员变量。

(10). Okio,其成员函数均为静态函数,负责读写超时机制实现及数据发送接收接口的实例化。

 

  • okhttp3.0.1源码分析

    以下,我们只分析okHttp3.0.1版本中HTTP1.1相关的源码,包含2个第三方jar包,okhttp-3.0.1.jar, okio-1.6.0.jar。其核心源码类主要包括但不限于 RealCall, HttpEngine, Dispatcher, Http1xStream, StreamAllocation, ConnectionPool, RealConnection, RealBufferedSource, RealBufferedSink。okhttp中使用比较多的是设计模式是:构建者模式和链式调用。

    在下面的分析中,我们暂时忽略以下几点:interceptor(拦截器)的源码流程(待后续。。。),即假设开发者没有使用interceptor拦截;followUp(重定向)的流程;cacheStrategy(缓存策略),cache(缓存)的流程。

  1. 开发者可直接调用的类或接口:
  1. OkHttpClient:开发者可以根据需要进行策略或参数配置,我们比较常用的参数配置是connectionTimeOut(连接超时), writeTimeOut(写超时), readTimeOut(读超时),retryOnConnectionFailure(连接重试)。
  2. Request:封装了HTTP请求参数,其采用了builder(构建者)模式。我们知道一个HTTP请求应该包括以下部分:HTTP方法,HTTP URL,HTTP header,如果是POST或PUT方法,还会有HTTP RequestBody。而开发者可以设置的正是以上几部分。
  3. Callback:请求回调接口,包含onFailure(失败,无应答),onResponse(成功)2个方法。
  4. okhttp支持异步请求和同步请求,我们目前使用的是异步请求,下面以异步请求为例。

 用户发起请求的异步调用流程如下:

  1. OkHttpClient.newCall(Request)生成RealCall实体类,然后调用RealCall.enqueue方法将用户请求任务AsyncCall加入Dispatcher队列中。

外部调用代码

okHttpClient.newCall(request).enqueue(new Callback());

RealCall类源码:

public void enqueue(Callback responseCallback) {

    this.enqueue(responseCallback, false);

}
//异步请求进入Dispatcher调度队列

void enqueue(Callback responseCallback, boolean forWebSocket) {

    synchronized(this) {

        if(this.executed) {

            throw new IllegalStateException("Already Executed");

        }
        this.executed = true;
    }
// client是初始化okhttp时,生成的全局唯一实例。
//创建AsyncCall实例,进入Dispatcher队列。forWebSocket表示的是websocket协议,在HTTP请求中,值为false。

    this.client.dispatcher().enqueue(new RealCall.AsyncCall(responseCallback, forWebSocket));

}

Dispatcher类源码:

Dispatcher类管理2个异步请求队列:runningAsyncCalls(正在执行的异步请求队列), readyAsyncCalls(在等待的异步请求队列)。runningAsycCalls队列的最大maxRequestsPerHost=5,maxRequests=64 。Dispatcher根据以上2个参数调度AsyncCall请求从readyAsyncCalls进入runningAsyncCalls队列。

Dispatcher类中负责执行任务的是ThreadPoolExecutor(线程池)executorService,其执行runningAsycCalls中的AsyncCall任务。AsyncCall实现了Runnable接口,executorService可直接execute(执行)此任务,这也是为什么不直接将request或者realCall加入任务队列,而要新增一个AsyncCall类的原因。 synchronized void enqueue(AsyncCall call) {

    if(this.runningAsyncCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) {

        this.runningAsyncCalls.add(call);

//进入线程池执行AsyncCall请求任务

        this.executorService().execute(call);

    } else {

        this.readyAsyncCalls.add(call);

    }

}
  1.  AsycCall任务执行过程:实际上调用的是RealCall.ApplicationInterceptorChain内部类proceed方法。

AsyncCall类源码:protected void execute() {

    boolean signalledCallback = false;

    try {

//同步调用RealCall的getResponseWithInterceptorChain方法

        Response e = RealCall.this.getResponseWithInterceptorChain(this.forWebSocket);
//当前请求是否被取消

        if(RealCall.this.canceled) {

            signalledCallback = true;

            this.responseCallback.onFailure(RealCall.this, new IOException("Canceled"));

        } else {
//获得http请求应答后,回调给外部应用程序callback

            signalledCallback = true;

            this.responseCallback.onResponse(RealCall.this, e);

        }

    } catch (IOException var6) {

        if(signalledCallback) {

            Internal.logger.log(Level.INFO, "Callback failure for " + RealCall.this.toLoggableString(), var6);

        } else {

            this.responseCallback.onFailure(RealCall.this, var6);

        }

    } finally {
//出队列

        RealCall.this.client.dispatcher().finished(this);

    }

}
  1.  ApplicationInterceptorChain类 proceed方法中调用的是RealCall的getResponse方法,(你一定很奇怪为什么不在步骤C中直接调用getResponse方法呢,那是因为ApplicationInterceptorChain中还实现了拦截器流程,这个我们在后续篇章中再分析)

RealCall源码:

上文我们已经说过,forWebSocket=false

private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {

    RealCall.ApplicationInterceptorChain chain = new RealCall.ApplicationInterceptorChain(0, this.originalRequest, forWebSocket);
//链式调用ApplicationInterceptorChain的proceed方法

    return chain.proceed(this.originalRequest);

}
ApplicationInterceptorChain类源码:public Response proceed(Request request) throws IOException {

//如果应用程序在外部增加了拦截器addInterceptor,则进入if分支

    if(this.index < RealCall.this.client.interceptors().size()) {

        RealCall.ApplicationInterceptorChain chain = RealCall.this.new ApplicationInterceptorChain(this.index + 1, request, this.forWebSocket);

        Interceptor interceptor = (Interceptor)RealCall.this.client.interceptors().get(this.index);

        Response interceptedResponse = interceptor.intercept(chain);

        if(interceptedResponse == null) {

            throw new NullPointerException("application interceptor " + interceptor + " returned null");

        } else {

            return interceptedResponse;

        }

    } else {
//调用RealCall的getResponse方法,下面我们将看到在getResponse方法内,完成了请求任务的核心流程。

        return RealCall.this.getResponse(request, this.forWebSocket);
    }

}
  1.  RealCall的getResponse方法做了下面几件事:

(1)修改http头部,如果有requestBody时,增加Content-Type头部域,重新校正Content-Length头部域;

(2)生成HttpEngine实体对象,从命名就可以看出HttpEngine应该是非常关键的类;

(3) 发送请求(this.engine.sendRequest);

(4) 读应答(this.engine.readResponse),你应该也看出来了,(3)和(4)都是HttpEngine对象实现的;

(5) 如果发生socket连接失败或者通信异常,判断是否满足重试条件,若满足,则循环步骤(2)、(3)、(4)

(5)释放资源。。。

但是千万不要被上述流程的方法名字迷惑,比如其实sendRequest中并没有发送请求,真正的请求发送操作是在readResponse中完成的。至于为什么是这样,我也有点疑惑2333。实际上engine.sendRequest中也是可以发送请求的,其被HttpEngine对象中一个boolean开关callerWritesRequestBody控制。步骤(2)构造HttpEngine实体对象时,传入的callerWritesRequestBody是false(这个boolean值是写死的,外部不能修改)。

RealCall getResponse()方法源码:

/**

   * 执行HTTP请求,返回HTTP应答

   */

  Response getResponse(Request request, boolean forWebSocket) throws IOException {

    //修改http头部,如果有requestBody时,增加Content-Type头部域,重新校正Content-Length头部域;

    RequestBody body = request.body();

    if (body != null) {

      Request.Builder requestBuilder = request.newBuilder();

      MediaType contentType = body.contentType();

      if (contentType != null) {

        requestBuilder.header("Content-Type", contentType.toString());

      }

      long contentLength = body.contentLength();

      if (contentLength != -1) {

        requestBuilder.header("Content-Length", Long.toString(contentLength));

        requestBuilder.removeHeader("Transfer-Encoding");

      } else {

        requestBuilder.header("Transfer-Encoding", "chunked");

        requestBuilder.removeHeader("Content-Length");

      }

      request = requestBuilder.build();

    }

    // 创建生成HTTP引擎实体,并完成重试和重定向

    engine = new HttpEngine(client, request, false, false, forWebSocket, null, null, null);

    int followUpCount = 0; //重定向计数器

    while (true) {

      if (canceled) {

        engine.releaseStreamAllocation();

        throw new IOException("Canceled");

      }

 

      boolean releaseConnection = true;

      try {

        engine.sendRequest();

        engine.readResponse();

        releaseConnection = false;

      } catch (RequestException e) {

        // The attempt to interpret the request failed. Give up.

                   //目前在okhttpv3.0.1源码中,针对HTTP1.1协议,没有地方抛出RequestException异常

        throw e.getCause();

      } catch (RouteException e) {

        // The attempt to connect via a route failed. The request will not have been sent.

                   //如果某个域名如google.com有多台服务器ip,当向其中一个服务器ip连接失败时,还可以通过其他的ip进行重试

        HttpEngine retryEngine = engine.recover(e.getLastConnectException(), null);

        if (retryEngine != null) {

          releaseConnection = false;

          engine = retryEngine;

          continue;

        }

        // Give up; recovery is not possible.

        throw e.getLastConnectException();

      } catch (IOException e) {

        // An attempt to communicate with a server failed. The request may have been sent.

           //如果某个域名如google.com有多台服务器ip,当与其中一个服务器ip通信失败时,还可以与其他的ip进行重试;或者由于某种原因,从连接池取出的复用连接实际上已经断开了与服务器ip的连接时,也可以重新通信。

        HttpEngine retryEngine = engine.recover(e, null);

        if (retryEngine != null) {

          releaseConnection = false;

          engine = retryEngine;

          continue;

        }

 

        // Give up; recovery is not possible.

        throw e;

      } finally {

        // We're throwing an unchecked exception. Release any resources.

        if (releaseConnection) {

          StreamAllocation streamAllocation = engine.close();

          streamAllocation.release();

        }

      }

//重定向,本文不分析重定向流程

      Response response = engine.getResponse();

      Request followUp = engine.followUpRequest();

      if (followUp == null) {

        if (!forWebSocket) {

          engine.releaseStreamAllocation();

        }

        return response;

      }

      StreamAllocation streamAllocation = engine.close();

      if (++followUpCount > MAX_FOLLOW_UPS) {

        streamAllocation.release();

        throw new ProtocolException("Too many follow-up requests: " + followUpCount);

      }

      if (!engine.sameConnection(followUp.url())) {

        streamAllocation.release();

        streamAllocation = null;

      }

      request = followUp;

      engine = new HttpEngine(client, request, false, false, forWebSocket, streamAllocation, null,

          response);

    }// while循环体结束

  }

下一篇文章,我们将继续分析建立socket连接、连接复用、发送流及接收流的详细流程。 下面附上RealCall getResponse()方法的流程图 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值