框架
okhttp
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(new Cache(getCacheDir(),1000))
.addInterceptor() // 应用拦截器,放在最前面
.addNetworkInterceptor() //网络拦截器,倒数第二个,在callserver 之前
.dns() //设置httpdns
.proxy() //设置代理
.build();
okHttpClient.newCall(request) 返回realcall
同步 execute
override fun execute(): Response {
//1,查看是否已经执行过,原子类,期望是false,是的话改为true
check(executed.compareAndSet(false, true)) { "Already Executed" }
timeout.enter()
callStart()
try {
//2,进入分发器,加入同步队列,总共有3个队列,同步,异步进行,异步准备
client.dispatcher.executed(this)
//3,通过拦截器进行请求
return getResponseWithInterceptorChain()
} finally {
//4,执行完毕,
client.dispatcher.finished(this)
}
}
//dispatcher 需要注意,新版本分发器,finish方法都会走promoteAndExecute()
//执行异步请求获取判断
private fun finished(calls: Deque, call: T) {
val isRunning = promoteAndExecute() //这个异步队列里 获取请求进行执行的方法
}
异步
override fun enqueue(responseCallback: Callback) {
//1,检查call有没有被用
check(executed.compareAndSet(false, true)) { "Already Executed" }
callStart()
//2 ,进行分发,注意这边给的是AsyncCall,同步直接给分发realcall
client.dispatcher.enqueue(AsyncCall(responseCallback))
}
//Dispatcher
internal fun enqueue(call: AsyncCall) {
synchronized(this) {
//1,加入准备队列
readyAsyncCalls.add(call)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
// the same host.
//2,判断如果不是websocket请求
if (!call.call.forWebSocket) {
//2.1 找到是否存在这个host的请求
val existingCall = findExistingCallWithHost(call.host)
//2.2存在的话 callsPerHost 指向 同样host的callsPerHost 值
if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)
}
}
//3 开始从异步准备队列获取
promoteAndExecute()
}
//3
private fun promoteAndExecute(): Boolean {
synchronized(this) {
val i = readyAsyncCalls.iterator()
while (i.hasNext()) {
val asyncCall = i.next()
//1,判断,请求数 有没有超过64个,有就结束
if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.
// 2,判读啊这个host的请求 有没有超过5,有超过,就拿下一个
if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.
//3,请求队列移除
i.remove()
//5,callsPerHost + 1 这个主要用来判断同个host请求超过5没
asyncCall.callsPerHost.incrementAndGet()
// 这个是干嘛呢,比如我前面一个请求没有,那我一次可以添加到第64个
executableCalls.add(asyncCall)
// 6,加入云队列
runningAsyncCalls.add(asyncCall)
}
isRunning = runningCallsCount() > 0
}
for (i in 0 until executableCalls.size) {
val asyncCall = executableCalls[i]
//并行执行这些请求,
//executorService = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS,
SynchronousQueue(), threadFactory("$okHttpName Dispatcher", false))
//差不多就是 Cachepool
asyncCall.executeOn(executorService)
}
return isRunning
}
fun executeOn(executorService: ExecutorService) {
var success = false
try {
executorService.execute(this)
success = true
} catch (e: RejectedExecutionException) {
} finally {
//其实是成功失败 都会调用finished 方法,那么为什么这边只有!success呢
//因为run里已经执行了
if (!success) {
client.dispatcher.finished(this) // This call is no longer running!
}
}
}
override fun run() {
try {
//通过拦截器进行请求
val response = getResponseWithInterceptorChain()
signalledCallback = true
responseCallback.onResponse(this@RealCall, response)
} catch (e: IOException) {
} finally {
//调用分发器的finish方法
client.dispatcher.finished(this)
}
}
}
}
internal fun finished(call: AsyncCall) {
//这边和直接realcall不同,她会先加吧callsPerHost -1
call.callsPerHost.decrementAndGet()
finished(runningAsyncCalls, call)
}
RetryAndFollowUpInterceptor 重定重试向拦截器
1,会根据请求返回结果,判断是否重试重定向
override fun intercept(chain: Interceptor.Chain): Response {
//循环,方便失败以后重试请求
while (true) {
var response: Response
try {
//1,用户主动取消的,不进行重试重定向
if (call.isCanceled()) {
throw IOException("Canceled")
}
try {
//执行到下一个拦截器
response = realChain.proceed(request)
} catch (e: RouteException) {
//出错了 recover 方法判断要不要进行重新请求
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.
//进行重定向请求
val followUp = followUpRequest(response, exchange)
//不会重试重定向
if (followUpBody != null && followUpBody.isOneShot()) {
closeActiveExchange = false
return response
}
//重定向超过20次,不再重定向
if (++followUpCount > MAX_FOLLOW_UPS) {
throw ProtocolException("Too many follow-up requests: $followUpCount")
}
request = followUp
priorResponse = response
} finally {
call.exitNetworkInterceptorExchange(closeActiveExchange)
}
}
}
private fun recover(
e: IOException,
call: RealCall,
userRequest: Request,
requestSendStarted: Boolean
): Boolean {
// The application layer has forbidden retries.
// 1, okhttp builder的时候设置了 retryOnConnectionFailure(false)
if (!client.retryOnConnectionFailure) return false
// We can't send the request body again.
//2,这次请求只请求一次
if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
// This exception is fatal.
//3,判断错误类型,比如io关闭,这种错误不会重试
if (!isRecoverable(e, requestSendStarted)) return false
//4,服务器返回了retryAfter
// No more routes to attempt.
if (!call.retryAfterFailure()) return false
// For failure recovery, use the same route selector with a new connection.
return
private fun followUpRequest(userResponse: Response, exchange: Exchange?): Request? {
val route = exchange?.connection?.route()
val responseCode = userResponse.code
val method = userResponse.request.method
when (responseCode) {
//这俩都是身份验证
HTTP_PROXY_AUTH -> {
return client.proxyAuthenticator.authenticate(route, userRes ponse)
}
HTTP_UNAUTHORIZED -> return client.authenticator.authenticate(route, userResponse)
//响应重定向,拿出头里location的地址进行重新请求
HTTP_PERM_REDIRECT, HTTP_TEMP_REDIRECT, HTTP_MULT_CHOICE, HTTP_MOVED_PERM, HTTP_MOVED_TEMP, HTTP_SEE_OTHER -> {
return buildRedirectRequest(userResponse, method)
}
//
HTTP_CLIENT_TIMEOUT -> {
// 408's are rare in practice, but some servers like HAProxy use this response code. The
// spec says that we may repeat the request without modifications. Modern browsers also
// repeat the request (even non-idempotent ones.)
if (!client.retryOnConnectionFailure) {
// The application layer has directed us not to retry the request.
return null
}
val requestBody = userResponse.request.body
if (requestBody != null && requestBody.isOneShot()) {
return null
}
val priorResponse = userResponse.priorResponse
//注意这里,连续两次超时,不会再请求
if (priorResponse != null && priorResponse.code == HTTP_CLIENT_TIMEOUT) {
// We attempted to retry and got another timeout. Give up.
return null
}
// 要求 retryAfter
if (retryAfter(userResponse, 0) > 0) {
return null
}
return userResponse.request
}
HTTP_UNAVAILABLE -> {
val priorResponse = userResponse.priorResponse
// 连续 俩次 HTTP_UNAVAILABLE
if (priorResponse != null && priorResponse.code == HTTP_UNAVAILABLE) {
// We attempted to retry and got another timeout. Give up.
return null
}
//同样retryAfter,需要获取0
if (retryAfter(userResponse, Integer.MAX_VALUE) == 0) {
// specifically received an instruction to retry without delay
return userResponse.request
}
return null
}
//重新选择ip请求,比如这个ip 已经满了
HTTP_MISDIRECTED_REQUEST -> {
// OkHttp can coalesce HTTP/2 connections even if the domain names are different. See
// RealConnection.isEligible(). If we attempted this and the server returned HTTP 421, then
// we can retry on a different connection.
val requestBody = userResponse.request.body
if (requestBody != null && requestBody.isOneShot()) {
return null
}
if (exchange == null || !exchange.isCoalescedConnection) {
return null
}
exchange.connection.noCoalescedConnections()
return userResponse.request
}
else -> return null
}
}
桥接拦截器 BridgeInterceptor
就是加头,content-type ,content-length,Accept-Encoding ,cookie这些
注意cookie okhttp提供我们保存重用的方法
new OkHttpClient.Builder().cookieJar(new CookieJar() {
@Override
public void saveFromResponse(@NonNull HttpUrl httpUrl, @NonNull List list) {
}
@NonNull
@Override
public List loadForRequest(@NonNull HttpUrl httpUrl) {
return null;
}
})
缓存拦截器
强缓存
就是我能通过时间自己判断要不要实用缓存
代表使用
etag :过期时间
cache - control :max-age
协商缓存 我必须和服务器协商,问下服务器我能不能用这个缓存
代表使用cache - control if-none-match = 上次拿到的 etag, if-modify-since = 上次拿到的lastmodify的值
如果没有变,不会返回新的数据,直接返回304
整个流程
先找有没有缓存 -> 判断上次缓存头类型 -》 判断用户这次请求是否请求缓存 比如用户设置了no-cache, if-modify-since 这些事协商缓存,那就直接请求网络 -> 判断缓存的有效性
链接拦截器
复用之前socket,必须要是信息全部一样,如ip端口http协议
最大支持5个空闲连接,空闲连接最多维持5分钟
新的连接会
1,会生成一个代理
普通代理 socket http
隧道代理 https,这个只能做纯转发,建立连接前需要先发一个connect
2,SSLSocket 可以通过这个建立ssl连接,通过alpn获取当前支持http1还是http2
3,通过系统默认的InetAddress 就能进行 dns解析,但是这个一般都是localdns 也就是接入运行商的,反馈比较慢,还容易被劫持。
thread {
val ip2: InetAddress = InetAddress.getByName("www.baidu.com")
Log.d("wwwwwww","ip2:"+ip2.getHostAddress())
Log.d("wwwwwww","ip2:"+ip2.getHostName())
}
请求服务拦截器
就是直接请求网络
问题1,okhttp拦截器怎么复用tcp连接
他有个缓存池RealConnectionPool 里面有个容器缓存了 RealConnection
必须连接信息完全一样才可以用
同时有定时清理机制最大空闲不超过5个,最大空闲时间不超过5分钟
Transfer-Encoding: chunked
分块传输,最后一块长度为0表示结束,因此不需要content-length