敞开成长之旅!这是我参加「日新计划 12 月更文应战」的第37天,点击查看活动概况

文章中源码的OkHttp版本为4.10.0

implementation 'com.squareup.okhttp3:okhttp:4.10.0'

OkHttp源码剖析(一)中首要剖析了运用、怎么创立,再到发起恳求;

OkHttp源码剖析(二)中首要剖析了OkHttp的拦截器链;

OkHttp源码剖析(三)中首要剖析了OkHttp的缓存策略;

那么在这篇文章中首要剖析OkHttp的衔接池怎么铲除闲暇衔接等内容。

1.OkHttp的衔接池

/**
 * 办理HTTP和HTTP/2衔接的重用,以减少网络延迟。同享相同地址的HTTP恳求能够同享一个衔接。
 */
class ConnectionPool internal constructor(
internal val delegate: RealConnectionPool
) {
    constructor(
        maxIdleConnections: Int,
        keepAliveDuration: Long,
        timeUnit: TimeUnit
    ) : this(RealConnectionPool(
        taskRunner = TaskRunner.INSTANCE,
        maxIdleConnections = maxIdleConnections,
        keepAliveDuration = keepAliveDuration,
        timeUnit = timeUnit
    ))
    constructor() : this(5, 5, TimeUnit.MINUTES)
    /** 回来池中闲暇衔接的数量。 */
    fun idleConnectionCount(): Int = delegate.idleConnectionCount()
    /** 回来池中的衔接总数。 */
    fun connectionCount(): Int = delegate.connectionCount()
    /** 封闭并删除池中的一切闲暇衔接。 */
    fun evictAll() {
    	delegate.evictAll()
    }
}
  • OkHttp支撑5可并发KeepLive,默认链路生命为5分钟,衔接池的最终实现是RealConectionPool,添加衔接到衔接池的代码如下
//RealConnectionPool#put
fun put(connection: RealConnection) {
    connection.assertThreadHoldsLock()
    //添加到衔接池
    connections.add(connection)
    //经过铲除队列的schedule,铲除闲暇时刻最长的衔接超越保持衔接约束或闲暇衔接约束的衔接
    cleanupQueue.schedule(cleanupTask)
}

2.OkHttp的闲暇衔接怎么铲除

private val cleanupTask = object : Task("$okHttpName ConnectionPool") {
	override fun runOnce() = cleanup(System.nanoTime())
}
/**
 * 在此池上履行维护,假如闲暇时刻最长的衔接超越保持衔接约束或闲暇衔接约束,则删除该衔接。
 * 回来休眠的持续时刻(以纳秒为单位),直到下一次预定调用此办法。假如不需要进一步整理,则回来-1。
 */
fun cleanup(now: Long): Long {
    //正在运用的衔接数量
    var inUseConnectionCount = 0
    //闲暇的衔接数量
    var idleConnectionCount = 0
    //最长闲暇衔接
    var longestIdleConnection: RealConnection? = null
    //最长闲暇时刻
    var longestIdleDurationNs = Long.MIN_VALUE
    // 找到要整理的衔接,或许找到下一次整理的截止时刻。
    for (connection in connections) {
        synchronized(connection) {
            // 假如衔接正在运用,继续查找
            if (pruneAndGetAllocationCount(connection, now) > 0) {
            	inUseConnectionCount++
            } else {
            	idleConnectionCount++
                // 假如衔接已经准备好被整理,咱们就完成了。
                //闲暇时刻 = 当时时刻 - 闲置时刻
                val idleDurationNs = now - connection.idleAtNs
                //假如闲暇时刻 > 最长闲暇时刻
                if (idleDurationNs > longestIdleDurationNs) {
                    //闲暇时刻赋值给最长闲暇时刻
                	longestIdleDurationNs = idleDurationNs
                    //当时connection赋值给最长闲暇衔接
                	longestIdleConnection = connection
                } else {
                	Unit
                }
            }
        }
    }
    when {
        //最长闲暇时刻 >= 存活时刻 || 闲暇的衔接数量 > 最大闲暇衔接数量
        longestIdleDurationNs >= this.keepAliveDurationNs
        || idleConnectionCount > this.maxIdleConnections -> {
            // 选择了一个前面查找到的衔接来整理。整理前再次确认它是否能够被整理,然后封闭
            val connection = longestIdleConnection!!
            synchronized(connection) {
                if (connection.calls.isNotEmpty()) return 0L // 不再闲暇
                if (connection.idleAtNs + longestIdleDurationNs != now) return 0L // 不再闲暇
                connection.noNewExchanges = true
                //铲除衔接
                connections.remove(longestIdleConnection)
            }
            connection.socket().closeQuietly()
            if (connections.isEmpty()) cleanupQueue.cancelAll()
            // 再次整理。
            return 0L
        }
        // 闲暇衔接数量 > 0
        idleConnectionCount > 0 -> {
        	// 衔接即将被铲除。
            // 回来 存活时刻 - 最长闲暇时刻
        	return keepAliveDurationNs - longestIdleDurationNs
        }
        // 正在运用的衔接数量 > 0
        inUseConnectionCount > 0 -> {
        	// 一切衔接都在运用中。在咱们再次运行之前,它至少是保持存活的时刻。
            // 回来衔接的存活时刻
        	return keepAliveDurationNs
        }
        else -> {
        	//没有衔接,闲暇或正在运用。
        	return -1
        }
    }
}
  • 闲暇衔接的铲除具体分为2个步骤:
    • 找到当时衔接是闲暇的并且这个衔接的闲暇时刻大于设定的最长闲暇时刻的衔接,只有这种衔接才能够被铲除;
    • 条件判别:最长闲暇时刻 >= 存活时刻 || 闲暇的衔接数量 > 最大闲暇衔接数量 ,符合这个条件后才能够经过remove铲除衔接,并且在铲除前还要进行确认是闲暇的衔接;
  • 两个特点值的获取:
    • 判别是否有闲暇衔接,假如有则回来闲暇衔接的闲暇时刻;
    • 判别是否有正在运用的衔接,如固有则回来衔接的存活时刻;

3.Application Interceptors与Network Interceptors

  • Application Interceptors是使用拦截器,Network Interceptors是网络拦截器;
  • 使用拦截器是用于在恳求发送前和网络呼应后的拦截器,只能触发一次。而网络拦截器在产生过错重试或许重定向时能够履行屡次,相当于进行了二次恳求;
  • 假如CacheInterceptor命中了缓存就不在进行网络恳求了,因此会存在短路网络拦截器的状况;
  • 使用拦截器通常用于计算客户端的网络恳求发起状况;网络拦截器中能够获取到最终发送恳求的request也包括重定向的数据,也能够获取真正产生网络恳求的回来的response,然后修改对应的恳求和呼应数据。