OkHttp请求流程源码解析(Okhttp版本4.9.1)
同学我之前对于Okhttp3版本进行了源码的分析(未完成),整体还是用java实现的,从okhttp4以后的版本开始都采用kotlin进行编写了,整体框架流程都是大体一样的,但是也有些改动,下面我们来看看它们会有什么不一样的烟火呢
同步和异步请求简单使用(以Get请求为例)
//以下都是Get请求
//同步请求
private fun excuteTest() {
//1. 新建一个okhttpClient
val okHttpClient = OkHttpClient()
//2. 新建request对象
val request = Request.Builder()
.url(BASE_URL)
.build()
//3.返回Response响应
okHttpClient.newCall(request).execute()
}
//异步请求
private fun asyncTest() {
//1.同样创建okhttpClient
val okHttpClient = OkHttpClient()
//2.新建request对象
val request = Request.Builder()
.url(BASE_URL)
.build()
//调用enqueue方法
okHttpClient.newCall(request).enqueue(object :Callback{
//失败回调
override fun onFailure(call: Call, e: IOException) {
}
//成功回调
override fun onResponse(call: Call, response: Response) {
}
})
}
新建Okhttpclient客户端
val okHttpClient = OkHttpClient()
constructor() : this(Builder())
可以看出OKHttp是使用建造者模式进行创建的,一般来说如果有很多参数携带的话,推荐使用Builder模式,看下Builder的可配置参数如下,可以看到构建了非常之多参数
//默认配置的构建参数
class Builder constructor() {
//分发器(核心部分)
internal var dispatcher: Dispatcher = Dispatcher()
//连接池
internal var connectionPool: ConnectionPool = ConnectionPool()
//拦截器(核心部分)
internal val interceptors: MutableList<Interceptor> = mutableListOf()
//网络拦截器
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
//重试链接失败
internal var retryOnConnectionFailure = true
internal var authenticator: Authenticator = Authenticator.NONE
//本地重定向,默认为true执行
internal var followRedirects = true
//安全套接层重定向
internal var followSslRedirects = true
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES
internal var cache: Cache? = null
internal var dns: Dns = Dns.SYSTEM
internal var proxy: Proxy? = null
internal var proxySelector: ProxySelector? = null
//代理身份验证
internal var proxyAuthenticator: Authenticator = Authenticator.NONE
internal var socketFactory: SocketFactory = SocketFactory.getDefault()
//安全套接层socket 工厂,用于HTTPS
internal var sslSocketFactoryOrNull: SSLSocketFactory? = null
internal var x509TrustManagerOrNull: X509TrustManager? = null
//传输层版本和运输协议(这里使用默认的)
internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS
internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier
//证书锁定,使用CertificatePinner来约束哪些认证机构被信任
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
//验证确认响应证书 适用 HTTPS 请求连接的主机名。
internal var certificateChainCleaner: CertificateChainCleaner? = null
internal var callTimeout = 0
internal var connectTimeout = 10_000
internal var readTimeout = 10_000
internal var writeTimeout = 10_000
internal var pingInterval = 0
internal var minWebSocketMessageToCompress = RealWebSocket.DEFAULT_MINIMUM_DEFLATE_SIZE
internal var routeDatabase: RouteDatabase? = null
//可以传入自己配置的构建参数
internal constructor(okHttpClient: OkHttpClient) : this() {
....
//大致和上面配置,代码较多暂不写出来了
}
同步请求流程
okHttpClient.newCall(request).execute()
看到newCall只是一个接口,RealCall才是真正的请求执行者
override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
RealCall中excute()方法的实现
override fun execute(): Response {
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
client.dispatcher.executed(this)
return getResponseWithInterceptorChain()
} finally {
client.dispatcher.finished(this)
}
}
-
其中check()方法是检查是否启动执行过,如果启动执行过的话就会抛出异常Already Executed,意味着每个Call他只能执行一次
-
直接看try/catch里,先通知dispatcher已经进入执行状态
-
调用getResponseWithInterceptorChain(),它是通过一系列的拦截器请求处理和响应处理得到最终的返回结果
-
无论什么时候,都会执行fianlly通知dispatcher自己已经执行完任务,调用Dispatcher的finish方法,可以看下
private fun <T> finished(calls: Deque<T>, call: T) {
val idleCallback: Runnable?
synchronized(this) {
if (!calls.remove(call)) throw AssertionError("Call wasn't in-flight!")
idleCallback = this.idleCallback
}
val isRunning = promoteAndExecute()
if (!isRunning && idleCallback != null) {
idleCallback.run()
}
}
finish方法主要是判断是否还有正在运行的请求队列,如果满足条件才能执行idleCallback的run方法
getRepsonseInterceptorChain()方法实现
@Throws(IOException::class)
internal fun getResponseWithInterceptorChain(): Response {
// Build a full stack of interceptors.
val interceptors = mutableListOf<Interceptor>()
//在配置OkttpClient设置的interceptors拦截器
interceptors += client.interceptors
//负责失败重试以及重定向
interceptors += RetryAndFollowUpInterceptor(client)
//请求的时候,对必要的header进行一些接受响应的时,必要时移除必要的header
interceptors += BridgeInterceptor(client.cookieJar)
// 负责读取缓存直接返回、更新缓存
interceptors += CacheInterceptor(client.cache)
//负责和服务器建立链接
interceptors += ConnectInterceptor
if (!forWebSocket) {
//配置okhttpClient设置的网络拦截器
interceptors += client.networkInterceptors
}
//负责向服务器发送请求数据,从服务器读取响应数据
interceptors += CallServerInterceptor(forWebSocket)
val chain = RealInterceptorChain(
call = this,
interceptors = interceptors,
index = 0,
exchange = null,
request = originalRequest,
connectTimeoutMillis = client.connectTimeoutMillis,
readTimeoutMillis = client.readTimeoutMillis,
writeTimeoutMillis = client.writeTimeoutMillis
)
var calledNoMoreExchanges = false
//使用责任链模式开启链式调用
try {
val response = chain.proceed(originalRequest)
if (isCanceled()) {
response.closeQuietly()
throw IOException("Canceled")
}
return response
} catch (e: IOException) {
calledNoMoreExchanges = true
throw noMoreExchanges(e) as Throwable
} finally {
if (!calledNoMoreExchanges) {
noMoreExchanges(null)
}
}
}
总结来说,getRepsonseInterceptorChain()主要实现了两件事,第一件事情创建拦截器集合,并把一个一个拦截器添加进来,第二件事情是调用RealInterceptorChain中的proceed方法,使用责任链模式开启链式调用,从而拿到实际完整的返回
proceed()方法实现
@Throws(IOException::class)
override fun proceed(request: Request): Response {
check(index < interceptors.size)
calls++
if (exchange != null) {
check(exchange.finder.sameHostAndPort(request.url)) {
"network interceptor ${interceptors[index - 1]} must retain the same host and port"
}
check(calls == 1) {
"network interceptor ${interceptors[index - 1]} must call proceed() exactly once"
}
}
// Call the next interceptor in the chain.\
//实例化下一个拦截器对应的RealInterceptorChain对象
val next = copy(index = index + 1, request = request)
//得到当前的拦截器
val interceptor = interceptors[index]
@Suppress("USELESS_ELVIS")
//调用当前拦截器的interceptor方法,并将下一个拦截器的RealInterceptorChain对象传递下去
val response = interceptor.intercept(next) ?: throw NullPointerException(
"interceptor $interceptor returned null")
if (exchange != null) {
check(index + 1 >= interceptors.size || next.calls == 1) {
"network interceptor $interceptor must call proceed() exactly once"
}
}
check(response.body != null) { "interceptor $interceptor returned a response with no body" }
return response
}
总的来说,proceed方法里大致就是实例化下一个拦截器对应的RealInterceptorChain对象,然后得到当前的拦截器,接着调用当前拦截器的interceptor方法,将下一个拦截器传递进来;以此,真正实现了链式调用,返回实际的响应response
异步请求流程
前面的操作步骤和同步请求是一致的,从开始调用dispatcher的enqueue方法开始看
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
//改变AsyncCall以便它共享现有运行调用的 AtomicInteger到同一个主机。
if (!call.call.forWebSocket) {
val existingCall = findExistingCallWithHost(call.host)
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
promoteAndExecute()
}
这里提前看下okhttp中的三个队列(正在准备的异步请求队列,运行中异步请求队列,同步请求队列)
/** Ready async calls in the order they'll be run. */
//准备就绪的异步队列
private val readyAsyncCalls = ArrayDeque<AsyncCall>()
/** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
//正在运行的异步请求队列
private val runningAsyncCalls = ArrayDeque<AsyncCall>()
/** Running synchronous calls. Includes canceled calls that haven't finished yet. */
//同步队列
private val runningSyncCalls = ArrayDeque<RealCall>()
可以看到enqueue方法中, 首先执行在 synchronized关键字代码块中,将所有call请求放入到准备就绪的异步请求队列,然后执行promoteAndExcute()方法
promoteAndExcute()方法实现
private fun promoteAndExecute(): Boolean {
this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>()
val isRunning: Boolean
synchronized(this) {
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
// 如果其中的runningAsynCalls不满,且call占用的host小于最大数量,则将call加入到runningAsyncCalls中执行,
// 同时利用线程池执行call;否者将call加入到readyAsyncCalls中。
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
i.remove()
asyncCall.callsPerHost.incrementAndGet()
executableCalls.add(asyncCall)
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
asyncCall.executeOn(executorService)
}
return isRunning
}
简单来说,这么一大串代码,放回值是通过一系列条件判断当前正在运行的异步请求数量是否大于0,换而言之,最后就是判断是否有正在执行的异步请求;在该方法里如果runningAsyncCalls不满,则将call请求放入到正在执行的异步请求队列中,然后利用线程池执行call,否则就放入到等待就绪队列中
看看AysncCall
internal inner class AsyncCall(
private val responseCallback: Callback
) : Runnable {
@Volatile var callsPerHost = AtomicInteger(0)
private set
fun reuseCallsPerHostFrom(other: AsyncCall) {
this.callsPerHost = other.callsPerHost
}
val host: String
get() = originalRequest.url.host
val request: Request
get() = originalRequest
val call: RealCall
get() = this@RealCall
/**
* Attempt to enqueue this async call on [executorService]. This will attempt to clean up
* if the executor has been shut down by reporting the call as failed.
*/
//线程池
fun executeOn(executorService: ExecutorService) {
client.dispatcher.assertThreadDoesntHoldLock()
var success = false
try {
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
val ioException = InterruptedIOException("executor rejected")
ioException.initCause(e)
noMoreExchanges(ioException)
responseCallback.onFailure(this@RealCall, ioException)
} finally {
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
override fun run() {
threadName("OkHttp ${redactedUrl()}") {
var signalledCallback = false
timeout.enter()
try {
//和同步执行一样,最终都会调用getResponseWithInterceptorChain()方法
val response = getResponseWithInterceptorChain()
signalledCallback = true
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)
} else {
responseCallback.onFailure(this@RealCall, e)
}
} catch (t: Throwable) {
cancel()
if (!signalledCallback) {
val canceledException = IOException("canceled due to $t")
canceledException.addSuppressed(t)
responseCallback.onFailure(this@RealCall, canceledException)
}
throw t
} finally {
client.dispatcher.finished(this)
}
}
}
}
可以看到,AsyncCall继承了Runnable, 通过线程池来管理异步任务,最后和同步请求一样都会调用getResponseWithInterceptorChain()方法
拦截器解析
综上所述,在拦截链的处理中,OKHttp默认帮我们做了五步拦截处理,包括RetryAndFollowInterceptor, BridgeInterceptor,CacheInterceptor, ConnectInterceptor, CallServerInterceptor
重定向拦截器RetryAndFollowInterceptor
我们来结合代码介绍下RetryAndFollowUpInterceptor主要做了哪些逻辑
- 首先获取以及初始化相关的实例.
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
var request = chain.request
val call = realChain.call
var followUpCount = 0
var priorResponse: Response? = null
var newExchangeFinder = true
var recoveredFailures = listOf<IOException>()
//这里进入死循环
while (true) {
...
}
}
- 这里获取了对应的RealInterceptorChain,RealCall对象,然后就进入一个死循环,让我们来看下死循环结构体中实现了什么操作
- 获取下层拦截器返回的结果,出现异常则根据是否可以恢复来判断中断,还是重新开始循环.
try {
if (call.isCanceled()) {
throw IOException("Canceled")
}
try {
response = realChain.proceed(request)
newExchangeFinder = true
} catch (e: RouteException) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) {
throw e.firstConnectException.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e.firstConnectException
}
newExchangeFinder = false
continue
} catch (e: IOException) {
// An attempt to communicate with a server failed. The request may have been sent.
if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) {
throw e.withSuppressed(recoveredFailures)
} else {
recoveredFailures += e
}
newExchangeFinder = false
continue
}
// Attach the prior response if it exists. Such responses never have a body.
if (priorResponse != null) {
response = response.newBuilder()
.priorResponse(priorResponse.newBuilder()
.body(null)
.build())
.build()
}
val exchange = call.interceptorScopedExchange
val followUp = followUpRequest(response, exchange)
if (followUp == null) {
if (exchange != null && exchange.isDuplex) {
call.timeoutEarlyExit()
}
closeActiveExchange = false
return response
}
val followUpBody = followUp.body
if (followUpBody != null && followUpBody.isOneShot()) {
closeActiveExchange = false
return response
}
response.body?.closeQuietly()
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
request = followUp
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
- 这里获取下层拦截的返回结果,然后根据返回的信息 判断是否需要重定向? 当重定向次数大于 MAX_FOLLOW_UPS = 20 时则抛出异常
- 然后判断重定向返回的信息是否出现异常?
- 出现则抛出异常并释放资源
- 不出现则用重定向返回的信息构建 request重新传给下层拦截器
桥接拦截器BridgeInterceptor
override fun intercept(chain: Interceptor.Chain): Response {
val userRequest = chain.request()
val requestBuilder = userRequest.newBuilder()
val body = userRequest.body
if (body != null) {
val contentType = body.contentType()
if (contentType != null) {
requestBuilder.header("Content-Type", contentType.toString())
}
val contentLength = body.contentLength()
if (contentLength != -1L) {
requestBuilder.header("Content-Length", contentLength.toString())
requestBuilder.removeHeader("Transfer-Encoding")
} else {
requestBuilder.header("Transfer-Encoding", "chunked")
requestBuilder.removeHeader("Content-Length")
}
}
if (userRequest.header("Host") == null) {
requestBuilder.header("Host", userRequest.url.toHostHeader())
}
if (userRequest.header("Connection") == null) {
requestBuilder.header("Connection", "Keep-Alive")
}
// If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
// the transfer stream.
var transparentGzip = false
if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
transparentGzip = true
requestBuilder.header("Accept-Encoding", "gzip")
}
val cookies = cookieJar.loadForRequest(userRequest.url)
if (cookies.isNotEmpty()) {
requestBuilder.header("Cookie", cookieHeader(cookies))
}
if (userRequest.header("User-Agent") == null) {
requestBuilder.header("User-Agent", userAgent)
}
val networkResponse = chain.proceed(requestBuilder.build())
cookieJar.receiveHeaders(userRequest.url, networkResponse.headers)
val responseBuilder = networkResponse.newBuilder()
.request(userRequest)
if (transparentGzip &&
"gzip".equals(networkResponse.header("Content-Encoding"), ignoreCase = true) &&
networkResponse.promisesBody()) {
val responseBody = networkResponse.body
if (responseBody != null) {
val gzipSource = GzipSource(responseBody.source())
val strippedHeaders = networkResponse.headers.newBuilder()
.removeAll("Content-Encoding")
.removeAll("Content-Length")
.build()
responseBuilder.headers(strippedHeaders)
val contentType = networkResponse.header("Content-Type")
responseBuilder.body(RealResponseBody(contentType, -1L, gzipSource.buffer()))
}
}
return responseBuilder.build()
}
- 不难看出,桥接拦截器是对http的添加了相应的header信息,如果返回的数据是gzip的,可以通过GzipResource进行解压直接获取repsonse(用户可用的网络请求响应)
- 简单来说桥接拦截器将用户构建Request转化为能够进行网络访问的请求
缓存拦截器CacheInterceptor
okhttp使用缓存非常简单,只需要在创建okhttpClient的时候调用cache就可以了,下面来看看CacheInterceptor是如何进行实现的
override fun intercept(chain: Interceptor.Chain): Response {
val call = chain.call()
//根据request得到cache中缓存的repsonse
val cacheCandidate = cache?.get(chain.request())
val now = System.currentTimeMillis()
// request判断缓存的策略,是否要使用了网络,缓存或两者都使用
val strategy = CacheStrategy.Factory(now, chain.request(), cacheCandidate).compute()
val networkRequest = strategy.networkRequest
val cacheResponse = strategy.cacheResponse
cache?.trackResponse(strategy)
val listener = (call as? RealCall)?.eventListener ?: EventListener.NONE
if (cacheCandidate != null && cacheResponse == null) {
// The cache candidate wasn't applicable. Close it.
cacheCandidate.body?.closeQuietly()
}
// If we're forbidden from using the network and the cache is insufficient, fail.
if (networkRequest == null && cacheResponse == null) {
return Response.Builder()
.request(chain.request())
.protocol(Protocol.HTTP_1_1)
.code(HTTP_GATEWAY_TIMEOUT)
.message("Unsatisfiable Request (only-if-cached)")
.body(EMPTY_RESPONSE)
.sentRequestAtMillis(-1L)
.receivedResponseAtMillis(System.currentTimeMillis())
.build().also {
listener.satisfactionFailure(call, it)
}
}
// If we don't need the network, we're done.
if (networkRequest == null) {
return cacheResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.build().also {
listener.cacheHit(call, it)
}
}
if (cacheResponse != null) {
listener.cacheConditionalHit(call, cacheResponse)
} else if (cache != null) {
listener.cacheMiss(call)
}
var networkResponse: Response? = null
try {
//调用下一个拦截器,决定从网络上得到repsonse
networkResponse = chain.proceed(networkRequest)
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (networkResponse == null && cacheCandidate != null) {
cacheCandidate.body?.closeQuietly()
}
}
// If we have a cache response too, then we're doing a conditional get.
//如果cacheResponse不为空,也就是本地存在cacheResponse,那么让它和网络得到的networkResponse做比较,决定是否来更新缓存的cacheResponse
if (cacheResponse != null) {
if (networkResponse?.code == HTTP_NOT_MODIFIED) {
val response = cacheResponse.newBuilder()
.headers(combine(cacheResponse.headers, networkResponse.headers))
.sentRequestAtMillis(networkResponse.sentRequestAtMillis)
.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis)
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
networkResponse.body!!.close()
// Update the cache after combining headers but before stripping the
// Content-Encoding header (as performed by initContentStream()).
cache!!.trackConditionalCacheHit()
cache.update(cacheResponse, response)
return response.also {
listener.cacheHit(call, it)
}
} else {
cacheResponse.body?.closeQuietly()
}
}
val response = networkResponse!!.newBuilder()
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
.build()
if (cache != null) {
if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
// Offer this request to the cache.
//缓存那些未经过缓存的response
val cacheRequest = cache.put(response)
return cacheWritingResponse(cacheRequest, response).also {
if (cacheResponse != null) {
// This will log a conditional cache miss only.
listener.cacheMiss(call)
}
}
}
if (HttpMethod.invalidatesCache(networkRequest.method)) {
try {
cache.remove(networkRequest)
} catch (_: IOException) {
// The cache cannot be written.
}
}
}
return response
}
- cache不为空从中获取cacheCandidate
- CacheStrategy.Factory获取到缓存策略,获取网络请求与响应缓存
- 调用cache的同步方法trackResponse,保证请求一致性
- 没网和没缓存的请求下直接返回一个504错误响应(HTTP_GATEWAY_TIMEOUT)
- 没网的情况下直接通过响应缓存创建repsonse并返回
- 有响应缓存(cacheResponse != null)并且响应码为304时(networkResponse.code() == HTTP_NOT_MODIFIED)--该响应码意思直接使用缓存。通过cacheResponse创建Response并返回。
- cache不为空的时候,然后有请求头和缓存策略的时候,通过cache的put方法进行缓存
缓存拦截器会根据请求的信息和缓存的响应的信息来判断是否存在缓存可用,如果有可以使用的缓存,那么就返回该缓存给用户,否则就继续使用责任链模式来从服务器中获取响应。当获取到响应的时候,又会把响应缓存到磁盘上面。
连接拦截器ConnectInterceptor
这里可以看下新版本的ConnectInterceptor,做了较大改动,主要作用是建立连接
object ConnectInterceptor : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val realChain = chain as RealInterceptorChain
val exchange = realChain.call.initExchange(chain)
val connectedChain = realChain.copy(exchange = exchange)
return connectedChain.proceed(realChain.request)
}
}
- 这里主要做的首先打开目标服务器的连接,调用proceed方法,依旧是将response响应结果返回,重点的是调用了initExchange()方法,它是RealCall中的一个方法
- initExChange()方法
internal fun initExchange(chain: RealInterceptorChain): Exchange {
synchronized(this) {
check(expectMoreExchanges) { "released" }
check(!responseBodyOpen)
check(!requestBodyOpen)
}
val exchangeFinder = this.exchangeFinder!!
val codec = exchangeFinder.find(client, chain)
val result = Exchange(this, eventListener, exchangeFinder, codec)
this.interceptorScopedExchange = result
this.exchange = result
synchronized(this) {
this.requestBodyOpen = true
this.responseBodyOpen = true
}
if (canceled) throw IOException("Canceled")
return result
}
可以看到它返回了Exchange,看下它的构造方法,传入了exchangeFinder参数, 这个是RetryAndFollowInterceptor重定向拦截器生成的,然后调用了它的find方法,我们继续朔源
- exchangeFinder.find()方法
fun find(
client: OkHttpClient,
chain: RealInterceptorChain
): ExchangeCodec {
try {
val resultConnection = findHealthyConnection(
connectTimeout = chain.connectTimeoutMillis,
readTimeout = chain.readTimeoutMillis,
writeTimeout = chain.writeTimeoutMillis,
pingIntervalMillis = client.pingIntervalMillis,
connectionRetryEnabled = client.retryOnConnectionFailure,
doExtensiveHealthChecks = chain.request.method != "GET"
)
return resultConnection.newCodec(client, chain)
} catch (e: RouteException) {
trackFailure(e.lastConnectException)
throw e
} catch (e: IOException) {
trackFailure(e)
throw RouteException(e)
}
}
- find()方法返回的是ExchangeCodec(Encodes HTTP requests and decodes HTTP responses), 可以看下resultConnection的newCodec方法,这里就是判断是否是HTTP2来生成HTTP2或者HTTP1的编码解码器
- 简单来说是对HTTP 协议操作的抽象,有两个实现:Http1ExchangeCodec和Http2ExchangeCodec,顾名思义,它们分别对应 HTTP/1.1 和 HTTP/2 版本的实现,在这个方法的内部实现连接池的复用处理
- 这里有个resultConnection,它是findHealthyConnection()生成的,我们继续朔源
- findHealthyConnection()方法
@Throws(IOException::class)
private fun findHealthyConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean,
doExtensiveHealthChecks: Boolean
): RealConnection {
while (true) {
val candidate = findConnection(
connectTimeout = connectTimeout,
readTimeout = readTimeout,
writeTimeout = writeTimeout,
pingIntervalMillis = pingIntervalMillis,
connectionRetryEnabled = connectionRetryEnabled
)
// Confirm that the connection is good.
if (candidate.isHealthy(doExtensiveHealthChecks)) {
return candidate
}
// If it isn't, take it out of the pool.
candidate.noNewExchanges()
// Make sure we have some routes left to try. One example where we may exhaust all the routes
// would happen if we made a new connection and it immediately is detected as unhealthy.
if (nextRouteToTry != null) continue
val routesLeft = routeSelection?.hasNext() ?: true
if (routesLeft) continue
val routesSelectionLeft = routeSelector?.hasNext() ?: true
if (routesSelectionLeft) continue
throw IOException("exhausted all routes")
}
}
这里返回的是RealConnection,发现它最终调用的是findConnection()方法,这里只是对RealConnection对象做了简单的操作
- findConnection()方法
@Throws(IOException::class)
private fun findConnection(
connectTimeout: Int,
readTimeout: Int,
writeTimeout: Int,
pingIntervalMillis: Int,
connectionRetryEnabled: Boolean
): RealConnection {
if (call.isCanceled()) throw IOException("Canceled")
// Attempt to reuse the connection from the call.
// 尝试使用已经分配的连接,已经分配的连接可能已经被限制
val callConnection = call.connection // This may be mutated by releaseConnectionNoEvents()!
if (callConnection != null) {
var toClose: Socket? = null
//释放当前连接的资源,如果该连接已经被限制创建新的流,或者是不是同一个host和端口,就返回一个Socket以关闭连接
synchronized(callConnection) {
if (callConnection.noNewExchanges || !sameHostAndPort(callConnection.route().address.url)) {
toClose = call.releaseConnectionNoEvents()
}
}
// If the call's connection wasn't released, reuse it. We don't call connectionAcquired() here
// because we already acquired it.
if (call.connection != null) {
check(toClose == null)
return callConnection
}
// The call's connection was released.
//关闭连接
toClose?.closeQuietly()
eventListener.connectionReleased(call, callConnection)
}
// We need a new connection. Give it fresh stats.
refusedStreamCount = 0
connectionShutdownCount = 0
otherFailureCount = 0
// Attempt to get a connection from the pool.
//尝试从连接池中获取一个连接
if (connectionPool.callAcquirePooledConnection(address, call, null, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
// Nothing in the pool. Figure out what route we'll try next.
val routes: List<Route>?
val route: Route
if (nextRouteToTry != null) {
// Use a route from a preceding coalesced connection.
routes = null
route = nextRouteToTry!!
nextRouteToTry = null
} else if (routeSelection != null && routeSelection!!.hasNext()) {
// Use a route from an existing route selection.
routes = null
route = routeSelection!!.next()
} else {
// Compute a new route selection. This is a blocking operation!
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
this.routeSelector = localRouteSelector
}
val localRouteSelection = localRouteSelector.next()
routeSelection = localRouteSelection
routes = localRouteSelection.routes
if (call.isCanceled()) throw IOException("Canceled")
// Now that we have a set of IP addresses, make another attempt at getting a connection from
// the pool. We have a better chance of matching thanks to connection coalescing.
// 根据一系列的IP地址从连接池中获取一个链接
if (connectionPool.callAcquirePooledConnection(address, call, routes, false)) {
val result = call.connection!!
eventListener.connectionAcquired(call, result)
return result
}
route = localRouteSelection.next()
}
// Connect. Tell the call about the connecting call so async cancels work.
val newConnection = RealConnection(connectionPool, route)
call.connectionToCancel = newConnection
try {
newConnection.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
} finally {
call.connectionToCancel = null
}
call.client.routeDatabase.connected(newConnection.route())
// If we raced another call connecting to this host, coalesce the connections. This makes for 3
// different lookups in the connection pool!
//如果我们进行了另一个连接到该主机的host 请合并连接。 这使得在连接池中进行3次不同的查找
if (connectionPool.callAcquirePooledConnection(address, call, routes, true)) {
val result = call.connection!!
nextRouteToTry = route
newConnection.socket().closeQuietly()
eventListener.connectionAcquired(call, result)
return result
}
synchronized(newConnection) {
//将这个连接放入到连接池当中
connectionPool.put(newConnection)
call.acquireConnectionNoEvents(newConnection)
}
eventListener.connectionAcquired(call, newConnection)
return newConnection
}
- 由于代码都写了注释,整体看上去就大概是从一个连接池中拿到一个连接,然后将这个连接返回,并且说要进行3次不同的查找,我们进入到callAcquirePooledConnection方法中看看进行了哪些操作吧
- callAcquirePooledConnection()方法
fun callAcquirePooledConnection(
address: Address,
call: RealCall,
routes: List<Route>?,
requireMultiplexed: Boolean
): Boolean {
for (connection in connections) {
synchronized(connection) {
if (requireMultiplexed && !connection.isMultiplexed) return@synchronized
if (!connection.isEligible(address, routes)) return@synchronized
call.acquireConnectionNoEvents(connection)
return true
}
}
return false
}
- 显而易见,这里去遍历了所有连接,然后找到一个可用连接,返回true,如果没有的话就返回false;
- 然后该方法传入的参数主要是address(包含host和port端口的Address),RealCall实例,路由列表routes以及是否使用多路复用requireMultiplexed(这个是针对Http2)
- 回到之前findConnection()方法,我们来讨论下调用的3次不同的查找
//第一次
connectionPool.callAcquirePooledConnection(address, call, null, false)
//第二次
connectionPool.callAcquirePooledConnection(address, call, routes, false)
//第三次
connectionPool.callAcquirePooledConnection(address, call, routes, true)
我们来对比下3次调用的差异
- 第一次调用是只去拿不进行多路复用的连接
- 第二次一开始我认为也是和一次一样只拿不进行多路复用的连接,但是发现它传入了路由列表, 可以在callAcquirePooledConnection方法中看到,在isEligible中如果不是Http2将会直接返回false,所以这是针对HTTP2的,和多路复用并没有太大关系,看下isEligible的实现如下:
//代码过于长,这里我只拿了部分代表性的代码
// 1. This connection must be HTTP/2.
if (http2Connection == null) return false
// 2. The routes must share an IP address.
if (routes == null || !routeMatchesAny(routes)) return false
- 第三次就是只拿多路复用的连接
- 看看三次调用的时机
- 首先我们进行第一次调用去拿取连接,如果没有拿到呢?可以看出,它会从localRouteSelector中取出一组路由继续下一次的调用获取
// Compute a new route selection. This is a blocking operation!
var localRouteSelector = routeSelector
if (localRouteSelector == null) {
localRouteSelector = RouteSelector(address, call.client.routeDatabase, call, eventListener)
this.routeSelector = localRouteSelector
}
val localRouteSelection = localRouteSelector.next()
routeSelection = localRouteSelection
routes = localRouteSelection.routes
- 如果下一次还是拿不到连接呢?这时候可以看到我们要创建了一个连接了newConnection()
// Connect. Tell the call about the connecting call so async cancels work.
val newConnection = RealConnection(connectionPool, route)
call.connectionToCancel = newConnection
call.connectionToCancel = newConnection
try {
newConnection.connect(
connectTimeout,
readTimeout,
writeTimeout,
pingIntervalMillis,
connectionRetryEnabled,
call,
eventListener
)
} finally {
call.connectionToCancel = null
}
call.client.routeDatabase.connected(newConnection.route())
这里我就有个疑问了,这里都已经创建了连接,为什么还需要在调用一次去获取连接呢? 结合网上资料的描述,有人和我同样提出了这个疑问 简单解析了下,原因在于如果有两个请求同时去访问同一个ip地址,然后这两个请求都分别创建一个连接,这样就有两个连接了,造成资源的浪费。 所以这里调用连接池的意义在于第一个请求创建好连接后,下一个请求再去创建连接的时候,去连接池拿一次如果拿到了的话就把自己创建的连接通过nextRouteToTry去暂存起来。当真正可用的时候再取出来
- 小结一下ConnectionInterceptor拦截器
总的来说呢,在我的理解下,就是找到一个可用的连接,然后生成对应的编码解码器
请求服务拦截器CallServerInterceptor
这个拦截器作用就是去发起真正的网络请求,然后介绍服务器返回的响应,看下简单看下代码,由于过于冗长,我这里只展示部分代码
if (HttpMethod.permitsRequestBody(request.method) && requestBody != 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 ("100-continue".equals(request.header("Expect"), ignoreCase = true)) {
exchange.flushRequest()
responseBuilder = exchange.readResponseHeaders(expectContinue = true)
exchange.responseHeadersStart()
invokeStartEvent = false
}
if (responseBuilder == null) {
if (requestBody.isDuplex()) {
// Prepare a duplex body so that the application can send a request body later.
exchange.flushRequest()
val bufferedRequestBody = exchange.createRequestBody(request, true).buffer()
requestBody.writeTo(bufferedRequestBody)
} else {
// Write the request body if the "Expect: 100-continue" expectation was met.
val bufferedRequestBody = exchange.createRequestBody(request, false).buffer()
requestBody.writeTo(bufferedRequestBody)
bufferedRequestBody.close()
}
} else {
exchange.noRequestBody()
if (!exchange.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.
exchange.noNewExchangesOnConnection()
}
}
} else {
exchange.noRequestBody()
}
- 大致就是表明已经完成了http请求的request写入工作
response.newBuilder()
.body(exchange.openResponseBody(response))
.build()
- 这里表明了完成了读取响应的body信息
if ("close".equals(response.request.header("Connection"), ignoreCase = true) ||
"close".equals(response.header("Connection"), ignoreCase = true)) {
exchange.noNewExchangesOnConnection()
}
- 完成了关闭连接
这样看来,一个网络请求的发起结束到这个拦截器就完成了,放出一张已经大众化的框架流程图
尾言
综上所述,其中除了OKHttp的内部请求流程这点之外,缓存和连接以及拦截器这些内容都是很重要的点,对于OKHTTP源码的解析大致各位同学应该都有着自己的见解,说的不好的地方欢迎大佬们指点迷津,有些地方还是带着疑惑,希望继续完善