建议先对HTTP有个大概的了解:HTTP概述
okhttp原理解析之整体流程
okhttp原理解析之cookie
okhttp 缓存解析
[okhttp Dns解析](# 待续)
[okhttp 连接解析](# 待续)
[okhttp http解析](# 待续)
[okhttp https解析](# 待续)
[okhttp io解析](# 待续)
涉及网络开发,避免不了HTTP的,而Android中广泛使用的便是okhttp库了,为什么要用okhttp?本人觉得主要是:
- 实现了HTTP的各种特性:dns、连接复用、http、https、缓存、cookie等
- 扩展性强:可以添加拦截器,对请求或者响应均可进行拦截添加自己的处理逻辑
- 性能增强:使用okio对io进行了性能上的优化
本文便是okhttp原理的解析的开篇,也是“okhttp整体流程解析”文章。
okhttp使用构建的设计模式通过build来构建okhttp实例,接着使用责任链设计模式对拦截器逐一执行它们的职责。
okhttp实例构建
okhttp提供OkHttpClient作为客户端,也是okhttp请求的入口,通过OkHttpClient中静态类Builder来构建,Builder类提供各种方法来设置okhttp的各种属性,最终通过build方法来创建OkHttpClient,并将Builder中的各种属性赋值给OkHttpClient,大体代码如下:
public static final class Builder {
public Builder() {
//设置默认的http请求分派器
this.dispatcher = new Dispatcher();
//设置默认的支持的http协议版本
this.protocols = OkHttpClient.DEFAULT_PROTOCOLS;
//设置默认的支持的tls版本
this.connectionSpecs = OkHttpClient.DEFAULT_CONNECTION_SPECS;
//设置默认的事件监听器,用于观测okhttp每个阶段的事务,一般用于开发阶段
this.eventListenerFactory = EventListener.factory(EventListener.NONE);
//设置代理选择器
this.proxySelector = ProxySelector.getDefault();
//设置没有cookie
this.cookieJar = CookieJar.NO_COOKIES;
//设置默认的socket工厂类
this.socketFactory = SocketFactory.getDefault();
//设置域名验证类
this.hostnameVerifier = OkHostnameVerifier.INSTANCE;
//设置默认的证书认证
this.certificatePinner = CertificatePinner.DEFAULT;
//设置无代理身份认证
this.proxyAuthenticator = Authenticator.NONE;
//设置无身份认证
this.authenticator = Authenticator.NONE;
//设置连接池
this.connectionPool = new ConnectionPool();
//设置dns使用系统层的dns
this.dns = Dns.SYSTEM;
//设置允许ssl重连
this.followSslRedirects = true;
//设置允许重连
this.followRedirects = true;
//设置运行连接失败时重连
this.retryOnConnectionFailure = true;
//设置连接超时为10秒
this.connectTimeout = 10000;
//设置读取超时为10秒
this.readTimeout = 10000;
//设置发送超时为10秒
this.writeTimeout = 10000;
//设置ping间隔为0秒
this.pingInterval = 0;
}
//设置连接超时时间
public OkHttpClient.Builder connectTimeout(long timeout, TimeUnit unit) {
this.connectTimeout = Util.checkDuration("timeout", timeout, unit);
return this;
}
....
//添加建立连接前的拦截器
public OkHttpClient.Builder addInterceptor(Interceptor interceptor) {
if (interceptor == null) {
throw new IllegalArgumentException("interceptor == null");
} else {
this.interceptors.add(interceptor);
return this;
}
}
//添加建立连接后的拦截器
public OkHttpClient.Builder addNetworkInterceptor(Interceptor interceptor) {
if (interceptor == null) {
throw new IllegalArgumentException("interceptor == null");
} else {
this.networkInterceptors.add(interceptor);
return this;
}
}
...
//构建okhttpclient实例
public OkHttpClient build() {
return new OkHttpClient(this);
}
}
OkHttpClient(OkHttpClient.Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
...
}
OkHttpClient的构建过程其实就是设置Builder的各种属性(这些属性与OkHttpClient是一一对应的),再通过build方法new一个OkHttpClient,在OkHttpClient的构建方法中,将builder的各种一一对应的设置给OkHttpClient的各个属性。这种构建方式可以确保构建OkHttpClient实例的属性不被修改(OkHttpClient的属性是读公共,写是私有的),确保了安全性。
okhttp的http请求流程
构建OkHttpClient之后,接着调用OkHttpClient的newCall方法得到网络请求的Call类
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false);
}
newCall返回的call的实现类RealCall。
1、发起请求
okhttp网络请求分为同步请求和异步请求,通过RealCall来调用
1.1、同步请求
RealCall的execute方法是同步请求
public Response execute() throws IOException {
synchronized(this) {
//断言不能重复发起请求
if (this.executed) {
throw new IllegalStateException("Already Executed");
}
this.executed = true;
}
//当前堆栈追踪
this.captureCallStackTrace();
//通知开始请求事件
this.eventListener.callStart(this);
Response var2;
try {
//client即OkhttpClient,调用Dispatcher分配器executed方法进行同步调度
this.client.dispatcher().executed(this);
//进入责任链网络请求模式,并得到网络请求结果
Response result = this.getResponseWithInterceptorChain();
if (result == null) {
throw new IOException("Canceled");
}
var2 = result;
} catch (IOException var7) {
this.eventListener.callFailed(this, var7);
throw var7;
} finally {
this.client.dispatcher().finished(this);
}
//返回请求结果
return var2;
}
同步请求将请求放入Dispatcher的同步请求队列,然后调用getResponseWithInterceptorChain(责任链模式)完整HTTP请求,最终得到响应对象Response并返回调用者,同步请求整体即完成。具体怎么实现后续内容和章节会一一介绍。
1.2、异步请求
RealCall的enqueue方法是同步请求
public void enqueue(Callback responseCallback) {
synchronized(this) {
//断言不能重复发起请求
if (this.executed) {
throw new IllegalStateException("Already Executed");
}
this.executed = true;
}
//当前堆栈追踪
this.captureCallStackTrace();
//通知开始请求事件
this.eventListener.callStart(this);
//client即OkhttpClient,调用Dispatcher分配器enqueue方法进行异步调度
this.client.dispatcher().enqueue(new RealCall.AsyncCall(responseCallback));
}
异步请求构建一个异步请求RealCall.AsyncCall,并通过Dispatcher 来调度。此时异步并未完成HTTP的整体流程,请继续查看后续内容。
3、dispatcher分派器调度网络请求
不论同步还是异步,都使用到Dispatcher进行调度,我们来看看Dispatcher怎么实现同步与异步的调度:
public final class Dispatcher {
//最大请求数
private int maxRequests = 64;
//每个域名运行并发数
private int maxRequestsPerHost = 5;
//空闲回调
@Nullable
private Runnable idleCallback;
//线程池
@Nullable
private ExecutorService executorService;
//异步情况下:等待调度的请求队列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque();
//异步情况下:运行中请求队列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque();
//同步情况下:运行中请求队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque();
......
//异步请求
synchronized void enqueue(AsyncCall call) {
if (this.runningAsyncCalls.size() < this.maxRequests && this.runningCallsForHost(call) < this.maxRequestsPerHost) {
//异步队列正在运行的请求数 < 最大请求数 并且 当前请求域名下的并发请求数 < 每个域名最大并发请求数
//将请求任务加入异步运行中队列
this.runningAsyncCalls.add(call);
//将请求任务加入线程池中调度执行
this.executorService().execute(call);
} else {
//异步队列正在运行的请求数 > 最大请求数 或者 当前请求域名下的并发请求数 > 每个域名最大并发请求数
//将请求任务加入异步等待调度队列
this.readyAsyncCalls.add(call);
}
}
//取消网络请求
public synchronized void cancelAll() {
Iterator var1 = this.readyAsyncCalls.iterator();
AsyncCall call;
while(var1.hasNext()) {
//遍历异步等待中队列,调用cancel进行取消
call = (AsyncCall)var1.next();
call.get().cancel();
}
var1 = this.runningAsyncCalls.iterator();
while(var1.hasNext()) {
//遍历异步进行中队列,调用cancel进行取消
call = (AsyncCall)var1.next();
call.get().cancel();
}
var1 = this.runningSyncCalls.iterator();
while(var1.hasNext()) {
//遍历同步进行中队列,调用cancel进行取消
RealCall call = (RealCall)var1.next();
call.cancel();
}
}
//调度异步等待队列
private void promoteCalls() {
if (this.runningAsyncCalls.size() < this.maxRequests) {
//当前异步运行中的请求数 < 最大请求数
if (!this.readyAsyncCalls.isEmpty()) {
//异步等待队列不为空
Iterator i = this.readyAsyncCalls.iterator();
do {
//遍历异步等待队列
if (!i.hasNext()) {
return;
}
//等待的请求
AsyncCall call = (AsyncCall)i.next();
if (this.runningCallsForHost(call) < this.maxRequestsPerHost) {
//等待的请求的域名并发请求数 < 每个域名最大并发请求数
//将等待请求call从异步等待队列中移到异步运行中队列
i.remove();
this.runningAsyncCalls.add(call);
//将等待请求call加入线程池中调度执行
this.executorService().execute(call);
}
} while(this.runningAsyncCalls.size() < this.maxRequests);
}
}
}
//技术异步请求的域名当前并发请求数
private int runningCallsForHost(AsyncCall call) {
int result = 0;
Iterator var3 = this.runningAsyncCalls.iterator();
while(var3.hasNext()) {
//遍历异步运行中队列
//正运行中的异步请求
AsyncCall c = (AsyncCall)var3.next();
if (c.host().equals(call.host())) {
//如果参数call与c同域名
//则同域名并发请求数 + 1
++result;
}
}
return result;
}
//同步请求
synchronized void executed(RealCall call) {
//直接将请求加入同步请求队列
this.runningSyncCalls.add(call);
}
//异步请求结束
void finished(AsyncCall call) {
this.finished(this.runningAsyncCalls, call, true);
}
//同步请求结束
void finished(RealCall call) {
this.finished(this.runningSyncCalls, call, false);
}
//请求结束处理函数
//calls:对哪个队列进行移除操作
//call:哪个请求结束
//promoteCalls:是否需要调度下一个等待的请求,异步需要,同步不需要
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
//运行中数量
int runningCallsCount;
//空闲回调
Runnable idleCallback;
synchronized(this) {
//从指定队列中移除已结束的请求
if (!calls.remove(call)) {
throw new AssertionError("Call wasn't in-flight!");
}
if (promoteCalls) {
//如果需要调度下一个等待的请求(即如果是异步)
//调度下一个等待的请求
this.promoteCalls();
}
//计算运行中的数量
runningCallsCount = this.runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
//如果没有运行中的请求,且空闲处理器不为空,调用空闲处理器
idleCallback.run();
}
}
......
}
dispatcher对应同步请求而言,只是将请求放入同步队列,便于后续的取消、完成等操作。
dispatcher对异步请求而言,需要等待队列和运行队列来完成异步调度,在同一域名并发数不超额且总请求数不超额的情况下,调度运行队列,将请求放入线程池运行。
dispatcher主要用于处理并发性能的,如果没来一个http请求就马上发起请求,如果数量足够多,甚至可以占满cpu的开销,所以才有了dispatcher的分派处理器。这个特性是对异步而言,而同步并只能通过线程池来减缓cpu开销。
4、责任链模式实现HTTP请求
同步请求时将请求加入dispatcher的同步队列之后,接着调用getResponseWithInterceptorChain进入责任链完成HTTP的请求。
异步请求时将请求加入dispatcher的异步队列之后,会回调RealCall.AsyncCall的run方法,而RealCall.AsyncCall继承自NamedRunnable:
public abstract class NamedRunnable implements Runnable {
......
public final void run() {
......
try {
//调用execute方法
this.execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
RealCall.AsyncCall实现NamedRunnable的execute方法:
protected void execute() {
boolean signalledCallback = false;
try {
//进入责任链模式完成HTTP请求
Response response = RealCall.this.getResponseWithInterceptorChain();
if (RealCall.this.retryAndFollowUpInterceptor.isCanceled()) {
//请求已取消
signalledCallback = true;
//回调onFailure方法告知请求已取消
this.responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
//回调onResponse方法告知请求已完成
this.responseCallback.onResponse(RealCall.this, response);
}
}
......
}
异步请求最终也是调用getResponseWithInterceptorChain进入责任链模式完成HTTP请求,并使用responseCallback回调成功或者失败。自此异步整体流程完成,具体怎么实现后续内容和章节将一一介绍。
我们看看RealCall.getResponseWithInterceptorChain:
//责任链模式完成HTTP请求
Response getResponseWithInterceptorChain() throws IOException {
List<Interceptor> interceptors = new ArrayList();
//优先加入自定义的网络连接前的拦截器,默认是空的
interceptors.addAll(this.client.interceptors());
//添加okhttp自己的重连拦截器
interceptors.add(this.retryAndFollowUpInterceptor);
//添加okhttp的桥接拦截器(主要是cookie拦截处理)
interceptors.add(new BridgeInterceptor(this.client.cookieJar()));
//添加okhttp自己的缓存拦截器
interceptors.add(new CacheInterceptor(this.client.internalCache()));
//添加okhttp自己的连接拦截器
interceptors.add(new ConnectInterceptor(this.client));
if (!this.forWebSocket) {
//不是websocket情况下,添加自定义的网络连接之后的拦截器,默认是空的
interceptors.addAll(this.client.networkInterceptors());
}
//添加okhttp自己的网络交互连接器(即发送最终请求、接收原始响应等)
interceptors.add(new CallServerInterceptor(this.forWebSocket));
//创建责任链
Chain chain = new RealInterceptorChain(interceptors, (StreamAllocation)null, (HttpCodec)null, (RealConnection)null, 0, this.originalRequest, this, this.eventListener, this.client.connectTimeoutMillis(), this.client.readTimeoutMillis(), this.client.writeTimeoutMillis());
//发起责任链式处理器
return chain.proceed(this.originalRequest);
}
interceptors和networkInterceptors的区别在于:interceptors在网络连接强处理请求,最后处理响应;networkInterceptors在网络连接之后处理请求,先用interceptors处理响应。
RealInterceptorChain是责任链的实例,它内部通过按序(优先调度排在前面)一一调度interceptors的拦截器,再包装成RealInterceptorChain进行调度完成HTTP请求和HTTP响应。
public final class RealInterceptorChain implements Chain {
.....
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call, EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
//拦截器列表
this.interceptors = interceptors;
//请求建立的连接
this.connection = connection;
//流量分配管理器
this.streamAllocation = streamAllocation;
//http版本请求与响应处理器
this.httpCodec = httpCodec;
//调度第几个拦截器,默认是0,即从interceptors的第一个开始调度
this.index = index;
//请求内容
this.request = request;
//请求对象
this.call = call;
//事件监听
this.eventListener = eventListener;
//连接超时时间
this.connectTimeout = connectTimeout;
//接收(读)超时时间
this.readTimeout = readTimeout;
//发送(写)超时时间
this.writeTimeout = writeTimeout;
}
......
//调度链的职责
public Response proceed(Request request) throws IOException {
return this.proceed(request, this.streamAllocation, this.httpCodec, this.connection);
}
//调度链的职责
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec, RealConnection connection) throws IOException {
if (this.index >= this.interceptors.size()) {
//断言执行第几个拦截器不能超过拦截器的总个数
throw new AssertionError();
} else {
//请求次数加1
++this.calls;
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
//如果已建立连接,且连接的域名和端口号和请求的域名和端口号不一样,抛出异常
throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must retain the same host and port");
} else if (this.httpCodec != null && this.calls > 1) {
//如果已连接,且请求次数超过一次,抛出异常
throw new IllegalStateException("network interceptor " + this.interceptors.get(this.index - 1) + " must call proceed() exactly once");
} else {
//创建下一个需要执行的任务链
RealInterceptorChain next = new RealInterceptorChain(this.interceptors, streamAllocation, httpCodec, connection, this.index + 1, request, this.call, this.eventListener, this.connectTimeout, this.readTimeout, this.writeTimeout);
//取出本次任务链需要处理的拦截器
Interceptor interceptor = (Interceptor)this.interceptors.get(this.index);
//通过执行拦截器,再执行传入的下一个任务链,直至完成HTTP请求,并得到响应内容
Response response = interceptor.intercept(next);
if (httpCodec != null && this.index + 1 < this.interceptors.size() && next.calls != 1) {
//如果已连接,且下一个要执行的拦截器存在,且下一个要执行拦截器已执行过,则抛出异常
throw new IllegalStateException("network interceptor " + interceptor + " must call proceed() exactly once");
} else if (response == null) {
//如果响应为空,抛出异常
throw new NullPointerException("interceptor " + interceptor + " returned null");
} else if (response.body() == null) {
//如果响应没有body排除异常
throw new IllegalStateException("interceptor " + interceptor + " returned a response with no body");
} else {
//返回响应
return response;
}
}
}
}
}
责任链实现类RealInterceptorChain其实就是按下按序(优先调度排在前面)一一调度interceptors的拦截器,最终完整HTTP的请求,每个拦截器会被包装成责任链实现类,每个拦截器都各司其职完成各种的职责,再调用下一个职责直至完成HTTP请求,我们来看看默认情况下第一个责任链拦截器RetryAndFollowUpInterceptor:
//chain:下一个责任
public Response intercept(Chain chain) throws IOException {
//取出请求内容
Request request = chain.request();
//转为真实的下一个责任
RealInterceptorChain realChain = (RealInterceptorChain)chain;
//从下一个责任中获取请求对象
Call call = realChain.call();
//从下一个责任中获取监听对象
EventListener eventListener = realChain.eventListener();
//创建流量分派对象
this.streamAllocation = new StreamAllocation(this.client.connectionPool(), this.createAddress(request.url()), call, eventListener, this.callStackTrace);
//需要继续发起请求的数量(失败重连、重定向等)
int followUpCount = 0;
//先去的响应
Response priorResponse = null;
while(!this.canceled) {
//请求未取消
//释放连接
boolean releaseConnection = true;
Response response;
try {
//调用下一个责任,完成下一个下一个责任的任务,并从中得到响应
response = realChain.proceed(request, this.streamAllocation, (HttpCodec)null, (RealConnection)null);
//取消释放连接
releaseConnection = false;
} catch (RouteException var16) {
//出现路由异常
if (!this.recover(var16.getLastConnectException(), false, request)) {
//当前异常不可覆盖修正,则抛出异常
throw var16.getLastConnectException();
}
//当前异常可覆盖修正,继续循环内容
//取消释放连接
releaseConnection = false;
continue;
} catch (IOException var17) {
//出现IO异常
boolean requestSendStarted = !(var17 instanceof ConnectionShutdownException);
if (!this.recover(var17, requestSendStarted, request)) {
//当前异常不可覆盖修正,则抛出异常
throw var17;
}
//当前异常可覆盖修正,继续循环内容
//取消释放连接
releaseConnection = false;
continue;
} finally {
if (releaseConnection) {
//释放连接
//调用流量分配的streamFailed处理相关异常
this.streamAllocation.streamFailed((IOException)null);
//调用流量分配进行相关资源的释放:io、连接等
this.streamAllocation.release();
}
}
if (priorResponse != null) {
//父级响应不为空,重新构建响应对象
response = response.newBuilder().priorResponse(priorResponse.newBuilder().body((ResponseBody)null).build()).build();
}
//构建新需要继续发起请求的请求内容
Request followUp = this.followUpRequest(response);
if (followUp == null) {
//无需继续发起请求
if (!this.forWebSocket) {
//非websocket,则释放相关资源
this.streamAllocation.release();
}
//返回响应对象
return response;
}
//关闭响应对象body
Util.closeQuietly(response.body());
//需要继续发起请求数 + 1
++followUpCount;
if (followUpCount > 20) {
//如果需要继续发起请求数 > 20 个
//释放相关资源并抛出异常,表示请求无法完成
this.streamAllocation.release();
throw new ProtocolException("Too many follow-up requests: " + followUpCount);
}
if (followUp.body() instanceof UnrepeatableRequestBody) {
//如果需要继续发起请求的请求内容 是不可重复的
//释放相关资源并抛出异常,一般是服务器需要将一些空闲的连接关闭
this.streamAllocation.release();
throw new HttpRetryException("Cannot retry streamed HTTP body", response.code());
}
if (!this.sameConnection(response, followUp.url())) {
//如果响应对象和需要继续发起请求的连接不是同一个
//释放相关资源
this.streamAllocation.release();
//重新构建一个流量分配器
this.streamAllocation = new StreamAllocation(this.client.connectionPool(), this.createAddress(followUp.url()), call, eventListener, this.callStackTrace);
} else if (this.streamAllocation.codec() != null) {
//如果流量分配器的HTTP请求和响应处理器不为空,抛出异常,表示当前响应正在关闭其body
throw new IllegalStateException("Closing the body of " + response + " didn't close its backing stream. Bad interceptor?");
}
//设置需要继续发起请求的请求内容为下一次请求的请求内容
request = followUp;
//设置父亲响应对象
priorResponse = response;
}
//循环正常接收,表示请求已取消
//释放相关资源
this.streamAllocation.release();
//抛出请求已取消异常
throw new IOException("Canceled");
}
RetryAndFollowUpInterceptor的职责主要是构建StreamAllocation(流量分配,用户处理建立连接相关控制),接着就调用下一个责任执行下一个任务,再完成HTTP请求并得到响应对象,再对响应对象进行处理(RetryAndFollowUpInterceptor对Response的职责主要是确定是否需要继续发起请求,如果需要就按要求发起新的请求,如果不需要要么返回Response要么抛出异常)。
具体其他责任链的职责是什么,是okhttp其他章节进行解析的。
总结
okhttp的HTTP请求总体流程是使用OkhttpClient.Builder构建okhttp客户端OkhttpClient,调用OkhttpClient的newCall构建Http请求对象类RealCall,RealCall的execute实现同步请求,RealCall的enqueue实现异步请求。不论同步还是异步,最终都会通过责任链模式完成HTTP请求。
同步请求与异步请求的主要区别:
- 同步不需要线程池调度请求,而异步需要线程池调度(同步需要调用者自行线程池管理,防止过多占用CPU资源)
- 同步没有最大请求数和同一域名最大并发数的限制,而异步有
- 同步是在调用时直接返回响应结果,异步是通过回调方法回调成功或者失败
网友评论