OkHttp原理分析(五)

955 阅读3分钟

打开与目标服务器的连接,然后继续执行下一个拦截器

object ConnectInterceptor : Interceptor {

  @Throws(IOException::class)
  override fun intercept(chain: Interceptor.Chain): Response {
    val realChain = chain as RealInterceptorChain
    val request = realChain.request()
    val transmitter = realChain.transmitter()
    // 请求不为GET请求
    val doExtensiveHealthChecks = request.method != "GET"
    // 实例化交换器 
    val exchange = transmitter.newExchange(chain, doExtensiveHealthChecks)
    return realChain.proceed(request, transmitter, exchange)
  }
}
internal fun newExchange(chain: Interceptor.Chain, doExtensiveHealthChecks: Boolean): Exchange {
  synchronized(connectionPool) {
    check(!noMoreExchanges) { "released" }
    check(exchange == null) {
      "cannot make a new request because the previous response is still open: " +
          "please call response.close()"
    }
  }
  // exchangeFinder在RetryAndFollowUpInterceptor中实例化,实例化交换编码器
  val codec = exchangeFinder!!.find(client, chain, doExtensiveHealthChecks)
  // 默认eventListener中的方法没有实现
  val result = Exchange(this, call, eventListener, exchangeFinder!!, codec)
  synchronized(connectionPool) {
    this.exchange = result
    // 请求是否发送完成
    this.exchangeRequestDone = false
    // 响应是否接收完成 
    this.exchangeResponseDone = false
    return result
  }
}
fun find(
  client: OkHttpClient,
  chain: Interceptor.Chain,
  doExtensiveHealthChecks: Boolean
): ExchangeCodec {
  val connectTimeout = chain.connectTimeoutMillis()
  val readTimeout = chain.readTimeoutMillis()
  val writeTimeout = chain.writeTimeoutMillis()
  val pingIntervalMillis = client.pingIntervalMillis
  val connectionRetryEnabled = client.retryOnConnectionFailure
  try {
    val resultConnection = findHealthyConnection(
      connectTimeout = connectTimeout,
      readTimeout = readTimeout,
      writeTimeout = writeTimeout,
      pingIntervalMillis = pingIntervalMillis,
      connectionRetryEnabled = connectionRetryEnabled,
      doExtensiveHealthChecks = doExtensiveHealthChecks
    )
    return resultConnection.newCodec(client, chain)
  } catch (e: RouteException) {
    trackFailure()
    throw e
  } catch (e: IOException) {
    trackFailure()
    throw RouteException(e)
  }
}
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
    )
    // 如果是一个新连接,跳过检查连接的健康状态
    synchronized(connectionPool) {
      if (candidate.successCount == 0) {
        return candidate
      }
    }
    // 检查连接的健康状态,如果不健康,重新寻找连接
    if (!candidate.isHealthy(doExtensiveHealthChecks)) {
      candidate.noNewExchanges()
      continue
    }
    return candidate
  }
}
private fun findConnection(
  connectTimeout: Int,
  readTimeout: Int,
  writeTimeout: Int,
  pingIntervalMillis: Int,
  connectionRetryEnabled: Boolean
): RealConnection {
  var foundPooledConnection = false
  var result: RealConnection? = null
  var selectedRoute: Route? = null
  var releasedConnection: RealConnection?
  val toClose: Socket?
  synchronized(connectionPool) {
    if (transmitter.isCanceled) throw IOException("Canceled")
    hasStreamFailure = false // 这是新尝试
    // 已使用的连接是否健康
    releasedConnection = transmitter.connection
    toClose = if (transmitter.connection != null && transmitter.connection!!.noNewExchanges) {
      // 从连接池清除连接
      transmitter.releaseConnectionNoEvents()
    } else {
      null
    }   
    if (transmitter.connection != null) {
      // 已使用的连接是健康的
      result = transmitter.connection
      releasedConnection = null
    }

    if (result == null) {
      // 尝试从连接池获取连接
      if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, null, false)) {
        foundPooledConnection = true
        result = transmitter.connection
      } else if (nextRouteToTry != null) {
        // 取下一个路由
        selectedRoute = nextRouteToTry
        nextRouteToTry = null
      } else if (retryCurrentRoute()) {
        // 现在路由可用
        selectedRoute = transmitter.connection!!.route()
      }
    }
  }
  toClose?.closeQuietly()
  // 已使用连接不可用
  if (releasedConnection != null) {
    eventListener.connectionReleased(call, releasedConnection!!)
  }
  // 从连接池获取连接
  if (foundPooledConnection) {
    eventListener.connectionAcquired(call, result!!)
  }
  if (result != null) {
    // 已使用连接或连接池中连接
    return result!!
  }
  // 路由选择器
  var newRouteSelection = false
  if (selectedRoute == null && (routeSelection == null || !routeSelection!!.hasNext())) {
    newRouteSelection = true
    routeSelection = routeSelector.next()
  }
  var routes: List<Route>? = null
  synchronized(connectionPool) {
    if (transmitter.isCanceled) throw IOException("Canceled")
    if (newRouteSelection) {
      // 利用路由从连接池获取连接
      routes = routeSelection!!.routes
      if (connectionPool.transmitterAcquirePooledConnection(
          address, transmitter, routes, false)) {
        foundPooledConnection = true
        result = transmitter.connection
      }
    }
    if (!foundPooledConnection) {
      if (selectedRoute == null) {
        selectedRoute = routeSelection!!.next()
      }
      // 创建连接并立即将其分配。这使得异步cancel()可以中断我们即 
      // 将进行的握手。
      result = RealConnection(connectionPool, selectedRoute!!)
      connectingConnection = result
    }
  }

  // 我们第二次获取连接成功
  if (foundPooledConnection) {
    eventListener.connectionAcquired(call, result!!)
    return result!!
  }

  // TCP + TLS 握手
  result!!.connect(
    connectTimeout,
    readTimeout,
    writeTimeout,
    pingIntervalMillis,
    connectionRetryEnabled,
    call,
    eventListener
  )
  // 在失败列表中去除路由
  connectionPool.routeDatabase.connected(result!!.route())

  var socket: Socket? = null
  synchronized(connectionPool) {
    connectingConnection = null
    // 上次尝试连接合并,只有在我们尝试多个并发连接到同一主机时才会发生。
    if (connectionPool.transmitterAcquirePooledConnection(address, transmitter, routes, true)) {
      // 关闭我们创建的连接并返回池化连接。
      result!!.noNewExchanges = true
      socket = result!!.socket()
      result = transmitter.connection

      // 我们可以获得立即不健康的合并连接。在这种情况下,我们将重试我们刚刚 
      // 成功连接的路线。
      nextRouteToTry = selectedRoute
    } else {
      // 将连接放入连接池
      connectionPool.put(result!!)
      transmitter.acquireConnectionNoEvents(result!!)
    }
  }
  socket?.closeQuietly()

  eventListener.connectionAcquired(call, result!!)
  return result!!
}
fun transmitterAcquirePooledConnection(
    address: Address,
    transmitter: Transmitter,
    routes: List<Route>?,
    requireMultiplexed: Boolean
  ): Boolean {
    assert(Thread.holdsLock(this))
    for (connection in connections) {
      // 接收但不是HTTP2连接
      if (requireMultiplexed && !connection.isMultiplexed) continue
      // 如果此连接可以将流发送到“address”,则返回trueif (!connection.isEligible(address, routes)) continue
      // 将连接添加到执行器中
      transmitter.acquireConnectionNoEvents(connection)
      return true
    }
    return false
  }
fun connect(
  connectTimeout: Int,
  readTimeout: Int,
  writeTimeout: Int,
  pingIntervalMillis: Int,
  connectionRetryEnabled: Boolean,
  call: Call,
  eventListener: EventListener
) {
  check(protocol == null) { "already connected" }
  var routeException: RouteException? = null
  val connectionSpecs = route.address.connectionSpecs
  val connectionSpecSelector = ConnectionSpecSelector(connectionSpecs)
  if (route.address.sslSocketFactory == null) {
    // HTTP协议
    if (ConnectionSpec.CLEARTEXT !in connectionSpecs) {
      throw RouteException(UnknownServiceException(
        "CLEARTEXT communication not enabled for client"))
    }
    val host = route.address.url.host
    if (!Platform.get().isCleartextTrafficPermitted(host)) {
      throw RouteException(UnknownServiceException(
        "CLEARTEXT communication to $host not permitted by network security policy"))
    }
  } else {
    if (Protocol.H2_PRIOR_KNOWLEDGE in route.address.protocols) {
      throw RouteException(UnknownServiceException(
        "H2_PRIOR_KNOWLEDGE cannot be used with HTTPS"))
    }
  }

  while (true) {
    try {
      // 如果此路由通过HTTP代理隧道HTTPS,则返回trueif (route.requiresTunnel()) {
        // 使用代理通道创建HTTPS连接,包括创建rawSocket和tunnel
        connectTunnel(connectTimeout, readTimeout, writeTimeout, call, eventListener)
        if (rawSocket == null) {
          // 我们无法连接隧道,但正确关闭了我们的资源。
          break
        }
      } else {
        // 创建HTTP或HTTPS连接
        connectSocket(connectTimeout, readTimeout, call, eventListener)
      }
      // 根据协议创建socket
      establishProtocol(connectionSpecSelector, pingIntervalMillis, call, eventListener)
      eventListener.connectEnd(call, route.socketAddress, route.proxy, protocol)
      break
    } catch (e: IOException) {
      socket?.closeQuietly()
      rawSocket?.closeQuietly()
      socket = null
      rawSocket = null
      source = null
      sink = null
      handshake = null
      protocol = null
      http2Connection = null

      eventListener.connectFailed(call, route.socketAddress, route.proxy, null, e)

      if (routeException == null) {
        routeException = RouteException(e)
      } else {
        routeException.addConnectException(e)
      }

      if (!connectionRetryEnabled || !connectionSpecSelector.connectionFailed(e)) {
        throw routeException
      }
    }
  }

  if (route.requiresTunnel() && rawSocket == null) {
    throw RouteException(ProtocolException(
      "Too many tunnel connections attempted: $MAX_TUNNEL_ATTEMPTS"))
  }

  val http2Connection = this.http2Connection
  if (http2Connection != null) {
    synchronized(connectionPool) {
      // HTTP2缓冲区限制
      allocationLimit = http2Connection.maxConcurrentStreams()
    }
  }
}
internal fun newCodec(client: OkHttpClient, chain: Interceptor.Chain): ExchangeCodec {
  val socket = this.socket!!
  val source = this.source!!
  val sink = this.sink!!
  val http2Connection = this.http2Connection

  return if (http2Connection != null) {
    // HTTP2交换编解码器
    Http2ExchangeCodec(client, this, chain, http2Connection)
  } else {
    // HTTP1交换编解码器
    socket.soTimeout = chain.readTimeoutMillis()
    source.timeout().timeout(chain.readTimeoutMillis().toLong(), MILLISECONDS)
    sink.timeout().timeout(chain.writeTimeoutMillis().toLong(), MILLISECONDS)
    Http1ExchangeCodec(client, this, source, sink)
  }
}

总结

连接拦截器总结
连接拦截器的主要工作是获取交换器对象,获取的过程中包含了获取连接的操作(复用当前连接,连接池中获取或新建连接) 连接池是OkHttpClient中的对象,默认连接池中的连接的有效时间为5分钟,最大连接数为5个,连接同时可以被1个执行器使用。