连接¶
尽管您只提供 URL,OkHttp 使用三种类型规划到您的 Web 服务器的连接:URL、地址和路由。
URLs¶
URL(例如 https://github.com/square/okhttp
)是 HTTP 和互联网的基础。除了是万维网上一切资源的通用、分散式命名方案外,它们还指定了如何访问 Web 资源。
URL 是抽象的
- 它们指定调用可以是纯文本 (
http
) 或加密 (https
),但未指定应使用哪种加密算法。它们也没有指定如何验证对等方的证书(HostnameVerifier)或信任哪些证书(SSLSocketFactory)。 - 它们未指定是否应使用特定的代理服务器或如何通过该代理服务器进行身份验证。
它们也是具体的:每个 URL 标识一个特定的路径(例如 /square/okhttp
)和查询(例如 ?q=sharks&lang=en
)。每个 Web 服务器托管许多 URL。
地址¶
地址指定 Web 服务器(例如 github.com
)以及连接到该服务器所需的所有静态配置:端口号、HTTPS 设置和首选网络协议(例如 HTTP/2)。
共享相同地址的 URL 也可能共享底层 TCP socket 连接。共享连接具有显著的性能优势:更低的延迟、更高的吞吐量(由于 TCP 慢启动)和节省电池。OkHttp 使用一个 ConnectionPool 自动重用 HTTP/1.x 连接并多路复用 HTTP/2 连接。
在 OkHttp 中,地址的一些字段来自 URL(scheme、hostname、port),其余来自 OkHttpClient。
路由¶
路由提供实际连接到 Web 服务器所需的动态信息。这是要尝试的特定 IP 地址(通过 DNS 查询发现)、要使用的确切代理服务器(如果使用了 ProxySelector)以及要协商的 TLS 版本(对于 HTTPS 连接)。
单个地址可能有许多路由。例如,托管在多个数据中心的 Web 服务器在其 DNS 响应中可能会产生多个 IP 地址。
在有限情况下,如果连接失败,OkHttp 会重试某个路由。
- 通过 HTTP 代理进行 HTTPS 连接时,代理可能会发出身份验证质询。OkHttp 将调用代理 authenticator 并再次尝试。
- 使用多个 连接规范 进行 TLS 连接时,这些规范会按顺序尝试,直到 TLS 握手成功。
连接¶
使用 OkHttp 请求 URL 时,它会执行以下操作:
- 它使用 URL 和配置的 OkHttpClient 创建一个地址。此地址指定了我们如何连接到 Web 服务器。
- 它尝试从连接池中检索与该地址对应的连接。
- 如果在连接池中未找到连接,它会选择一个要尝试的路由。这通常意味着发出 DNS 请求以获取服务器的 IP 地址。然后,如果需要,它会选择一个 TLS 版本和代理服务器。
- 如果是新路由,它会通过建立直接 socket 连接、TLS 隧道(用于通过 HTTP 代理的 HTTPS)或直接 TLS 连接来进行连接。它会根据需要执行 TLS 握手。此步骤可能会针对隧道质询和 TLS 握手失败重试。
- 它发送 HTTP 请求并读取响应。
如果连接出现问题,OkHttp 会选择另一个路由并再次尝试。这使得 OkHttp 能够在服务器的部分地址无法访问时恢复。当池化连接陈旧或尝试的 TLS 版本不受支持时,这也很有用。
收到响应后,连接将返回到连接池,以便将来请求时可以重复使用。连接在一段时间不活动后会从连接池中移除。
快速回退¶
从 5.0 版本开始,OkHttpClient
支持快速回退,这是我们对 Happy Eyeballs RFC 6555 的实现。
使用快速回退,OkHttp 会同时尝试连接到多个 Web 服务器。它保留最先连接成功的路由,并取消所有其他路由。其规则如下:
- 优先交替使用来自不同地址家族(IPv6 / IPv4)的 IP 地址,从 IPv6 开始。
- 在最近一次尝试开始 250 毫秒后才开始新的尝试。
- 保留最先成功的 TCP 连接,并取消所有其他连接。
- 只竞争 TCP。只在获胜的 TCP 连接上尝试 TLS 握手。
如果 TCP 握手竞争的获胜者未能在 TLS 握手中成功,则会使用剩余的路由重新启动此过程。