我们来看RouteSelector做了什么工作,当new StreamAllocation时,会新建一个RouteSelector
//每一个新连接都会创建一个新的StreamAllocation
public StreamAllocation(ConnectionPool connectionPool, Address address, Call call,
EventListener eventListener, Object callStackTrace) {
this.connectionPool = connectionPool;
this.address = address;
this.call = call;
this.eventListener = eventListener;
this.routeSelector = new RouteSelector(address, routeDatabase(), call, eventListener);
this.callStackTrace = callStackTrace;
}
public RouteSelector(Address address, RouteDatabase routeDatabase, Call call,
EventListener eventListener) {
this.address = address;
this.routeDatabase = routeDatabase;
this.call = call;
this.eventListener = eventListener;
resetNextProxy(address.url(), address.proxy());
}
主要是resetNextProxy方法,address对象的url和proxy都是开发者配置进来的,分别是目标主机的url和要使用的代理服务器,如果开发者没有配置proxy,就看proxySelector有没有实现,proxySelector可以给不同的url配置不同的代理,如果proxySelector也没有实现,则使用默认的实现,为Proxy.NO_PROXY不用代理,直连目标主机。
当创建RouteSelector就初始化好了routeSelector.proxies对象,后面会根据这个代理列表来通过代理服务器访问目标地址。
private void resetNextProxy(HttpUrl url, Proxy proxy) {
if (proxy != null) {
//如果配置的代理服务器不为空,则使用配置的
proxies = Collections.singletonList(proxy);
} else {
//如果没有配置proxy,则使用proxySelector来选择代理服务器,proxySelector允许开发者自己实现,可以给不同的url配置不同的代理服务器
//如果开发者没有配置proxySelecor这里默认实现为不使用代理
List<Proxy> proxiesOrNull = address.proxySelector().select(url.uri());
proxies = proxiesOrNull != null && !proxiesOrNull.isEmpty()
? Util.immutableList(proxiesOrNull)
: Util.immutableList(Proxy.NO_PROXY);
}
nextProxyIndex = 0;
}
这里我们看到已经初始化好了proxies列表,我们回到connectInteceptor->streamAllocation的findConnection,获取连接对象的方法
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {
....省略代码(主要功能是通过当前地址去连接池中查找可用连接)....
//从连接池中找不到可用连接
boolean newRouteSelection = false;
//通过routeSelector查询dns服务器,解析出目标主机的ip地址,或者代理服务器的ip地址
if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {
newRouteSelection = true;
//routeSelection存储下一个代理服务器可用的所有ip地址,没有配置代理服务器则存储的目标主机的可用的所有ip地址
//routeSelector初始化时就初始化好了一个proxies列表,这个列表存储了代理服务器的列表,每一次next都切换到下一台
//代理服务器(没有配置代理时则为目标主机),并且解析出当前服务器的所有可用ip,包装到routeSelection中
routeSelection = routeSelector.next();
}
.... 省略代码(主要功能是使用上面的ip地址,重新去连接池找,还是找不到就新建连接,找到则返回)....
}
看下routeSelection=routeSelector.next()这个代码做了什么工作
public Selection next() throws IOException {
//如果配置的proxies列表用完了,则报错
if (!hasNext()) {
throw new NoSuchElementException();
}
//存储下一个代理服务器可用的代理地址
List<Route> routes = new ArrayList<>();
while (hasNextProxy()) {
//获取下一个代理服务器proxy,并通过dns解析它的所有ip地址(可能是服务器集群),将结果保存在inetSocketAddresses列表中
Proxy proxy = nextProxy();
//遍历当前代理服务器解析出来的ip地址
for (int i = 0, size = inetSocketAddresses.size(); i < size; i++) {
//将每个ip地址保存为Route对象
Route route = new Route(address, proxy, inetSocketAddresses.get(i));
if (routeDatabase.shouldPostpone(route)) {
//判断当前地址inetSocketAddresses是否之前请求过且失败了,是的话将这个地址做特殊处理
postponedRoutes.add(route);
} else {
routes.add(route);
}
}
//寻找到可用的代理服务器地址,则跳出,要立马去请求了,不能等寻找到所有的地址,再来请求,那样影响效率,如果当前的请求失败,
//则会记录到routeDatabase中,下次再进来就会通过上面的routeDatabase.shouldPostpone(route)做特殊处理
if (!routes.isEmpty()) {
break;
}
}
//如果上面的可用地址都为空,则最后尝试之前失败的代理地址,也就是postponedRoutes里的
if (routes.isEmpty()) {
routes.addAll(postponedRoutes);
postponedRoutes.clear();
}
//将当前代理服务器的可用ip地址封装成Selection对象返回
return new Selection(routes);
}
我们看下Proxy proxy = nextProxy();方法是怎么解析出ip地址的
private Proxy nextProxy() throws IOException {
if (!hasNextProxy()) {
throw new SocketException("No route to " + address.url().host()
+ "; exhausted proxy configurations: " + proxies);
}
//获取下一个代理服务器
Proxy result = proxies.get(nextProxyIndex++);
resetNextInetSocketAddress(result);
return result;
}
再看resetNextInetSocketAddress
private void resetNextInetSocketAddress(Proxy proxy) throws IOException {
//存储当前代理解析出的所有可用ip地址
inetSocketAddresses = new ArrayList<>();
String socketHost;
int socketPort;
if (proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.SOCKS) {
//如果proxy配置的是直连,则使用目标地址的域名和端口。
//socks协议我理解应该是整个网络已经被代理软件代理了,只需要正常的向目标服务器发请求,网络都会通过代理服务,
//然后代理服务器再解析目标地址去访问
socketHost = address.url().host();
socketPort = address.url().port();
} else {
SocketAddress proxyAddress = proxy.address();
if (!(proxyAddress instanceof InetSocketAddress)) {
throw new IllegalArgumentException(
"Proxy.address() is not an " + "InetSocketAddress: " + proxyAddress.getClass());
}
//如果是Http代理,则使用代理服务器的域名和端口
InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
socketHost = getHostString(proxySocketAddress);
socketPort = proxySocketAddress.getPort();
}
if (socketPort < 1 || socketPort > 65535) {
throw new SocketException("No route to " + socketHost + ":" + socketPort
+ "; port is out of range");
}
if (proxy.type() == Proxy.Type.SOCKS) {
//socks协议交由代理服务去dns解析
inetSocketAddresses.add(InetSocketAddress.createUnresolved(socketHost, socketPort));
} else {
eventListener.dnsStart(call, socketHost);
//上面已经将socketHost赋值为目标主机的域名(直连情况)或者代理服务器的域名(代理情况)
//这里调用dns去解析出ip地址,如果没有实现dns,则使用默认的构造Dns SYSTEM = new Dns()
//里面lookup调用的就是安卓的api:InetAddress.getAllByName(hostname),它会解析出要访问的ip地址
//由于可能是服务器集群,有可能解析出多个ip出来
List<InetAddress> addresses = address.dns().lookup(socketHost);
if (addresses.isEmpty()) {
throw new UnknownHostException(address.dns() + " returned no addresses for " + socketHost);
}
eventListener.dnsEnd(call, socketHost, addresses);
//把目标ip都加入到初始化列表中
for (int i = 0, size = addresses.size(); i < size; i++) {
InetAddress inetAddress = addresses.get(i);
inetSocketAddresses.add(new InetSocketAddress(inetAddress, socketPort));
}
}
}
至此,通过Proxy proxy = nextProxy() –> resetNextInetSocketAddress(Proxy proxy),就把要访问的ip地址(可能是配置的代理服务器,也可以是主机)初始化好,放入RouteSelector的 List inetSocketAddresses中了。剩下的就是把所有InetSocketAddress一一封装成Route组成的列表routes,然后返回new Selection(routes)给到findConnection处。