【Android】OkHttp 源码剖析系列(五)——路由选择与代理机制


本源码剖析系列基于 OkHttp 3.14

文章目录:

【Android】OkHttp 源码剖析系列(一)——请求的发起及拦截器机制概述

【Android】OkHttp 源码剖析系列(二)——拦截器大体流程分析

【Android】OkHttp 源码剖析系列(三)——缓存机制

【Android】OkHttp 源码剖析系列(四)——连接的建立概述

【Android】OkHttp 源码剖析系列(五)——路由选择与代理机制

【Android】OkHttp 源码剖析系列(六)——连接复用机制及连接的建立

【Android】OkHttp 源码剖析系列(七)——请求的发起及响应的读取

路由选择

当我们第一次尝试从连接池获取连接获取不到时,若检查发现路由选择器中没有可供选择的路由,首先会进行一次路由选择的过程,因为 HTTP 请求的过程中,需要先找到一个可用的路由,再根据代理协议规则与目标建立 TCP 连接。

Route

我们先了解一下 OkHttp 中的 Route 类:

它是一个用于描述一条路由的类,主要通过了代理服务器信息 proxy、连接目标地址 InetSocketAddress 来描述一条路由。由于代理协议不同,这里 InetSocketAddress 会有不同的含义:

  • 没有代理的情况下它包含的信息是经过了 DNS 解析的 IP 以及协议的端口号
  • SOCKS 代理的情况下,它包含了 HTTP 服务器的域名和协议端口号
  • HTTP 代理的情况下,它包含了代理服务器经过了 DNS 解析的 IP 地址及端口号

Proxy

接着我们了解一下 Proxy 类,它是由 Java 原生提供的:

它是一个用于描述代理服务器的类,主要包含了代理协议的类型以及代理服务器对应的 SocketAddress 类,有以下三种类型:

  • DIRECT:不使用代理
  • HTTP:HTTP 代理
  • SOCKS:SOCKS 代理

RouteSelector

在代码中是通过 RouteSelector.next 方法进行的路由选择的过程,RouteSelecter 是一个负责负责管理路由信息,并辅助选择路由的类。它主要有三个职责:

  1. 收集可用的路由
  2. 选择可用的路由
  3. 维护连接失败路由信息

下面我们对它的三个职责的实现分别进行介绍。

代理的收集

代理的收集过程在 RouteSelector 的构造函数中实现,RouteSelector 在创建 ExchangeFinder 时创建:

让我们看到 resetNextProxy 方法:

可以看到,它首先检查了一下我们的 address 中有没有用户设定的代理(通过 OkHttpClient 传入),若有用户设定的代理,则直接使用用户设定的代理。

若用户没有设定的代理,则尝试使用 ProxySelector.select 方法来获取代理列表。这里的 ProxySelector 也可以通过 OkHttpClient 进行设置,默认情况下会使用系统默认的 ProxySelector 来获取系统配置中的代理列表。

选择可用路由

在代理选择成功之后,会进行可用路由的选择工作,我们可以看到 RouteSelector.next 方法:

可以看到,上面的步骤主要是一个核心思想——优先采用普通的路由,如果实在找不到普通的路由,再去采用连接失败的路由

我们可以先看到 nextProxy 方法做了什么:

它主要就是在之前收集的代理列表中获取下一个代理的信息,并且调用 resetNextInetSocketAddress 方法根据代理协议获取对应的 Address 相关信息填入 inetSocketAddresses 中。

我们看到 resetNextInetSocketAddress 的实现:

上面主要是一些对不同代理的类型的处理,最后将解析后的地址填入了 inetSocketAddresses 中。其中代理类型分别有 DIRECTSOCKSHTTP 三种。

对于不同的代理类型,它分别有如下的处理:

  • DIRECT:经过 DNS 对目标服务器的地址进行解析,之后将解析后的 IP 地址及端口号填入
  • SOCKS:直接填入代理服务器的域名及端口号
  • HTTP:首先通过 DNS 对代理服务器地址进行解析,将解析后的 IP 地址及端口号填入

之后,它根据刚刚的 inetSocketAddress 构建出了对应的 Route 对象,然后调用了 routeDatabase.shouldPostpone(route) 判断它是否是连接失败的路由。若不是则直接返回,否则只有所有正常路由耗尽的情况下才会采用它。

维护连接失败的路由信息

OkHttp 采用了 RouteDatabase 类来维护连接失败的路由信息,可以看到它的实现:

可以看到,它维护了一个连接失败的路由 Set,如果连接失败则会调用它的 failed 方法将失败路由存储进队列,如果连接成功则会调用它的 connected 方法将这条路由从失败路由中移除。可以通过 shouldPostpone 方法判断一个路由是否是连接失败的。

返回路由信息

最后通过 RouteSelector.Selection 这个类返回了我们所选择的路由的信息。它的定义如下:

它的实现很简单,内部维护了一个路由列表。之后,寻找连接时就可以根据这个 Selection 来获取具体的 Route,并建立 TCP 连接了。

参考资料

OkHttp3中的代理与路由


Android Developer in GDUT