- 版本说明
本文只考虑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(缓存)的流程。
- 开发者可直接调用的类或接口:
- OkHttpClient:开发者可以根据需要进行策略或参数配置,我们比较常用的参数配置是connectionTimeOut(连接超时), writeTimeOut(写超时), readTimeOut(读超时),retryOnConnectionFailure(连接重试)。
- Request:封装了HTTP请求参数,其采用了builder(构建者)模式。我们知道一个HTTP请求应该包括以下部分:HTTP方法,HTTP URL,HTTP header,如果是POST或PUT方法,还会有HTTP RequestBody。而开发者可以设置的正是以上几部分。
- Callback:请求回调接口,包含onFailure(失败,无应答),onResponse(成功)2个方法。
- okhttp支持异步请求和同步请求,我们目前使用的是异步请求,下面以异步请求为例。
用户发起请求的异步调用流程如下:
- 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);
}
}
- 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);
}
}
- 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);
}
}
- 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()方法的流程图