继前面的几篇OKhttp的拦截器简单分析之后,对于后续Okhttp之间的分析自己也着实琢磨了一段时间,是分析RealConnection?还是ConnectionPool,随着对Okhttp源码的深入分析发现,着实是千丝万缕,有点凌乱,所以对于Okttp的源码分析只能一点点的抽丝剥茧,本篇就来简单分析一下RouteSelector这个类。
RouteSelector的初始化:
RouteSelector这个类是在StreamAllocation里面初始化的:

public StreamAllocation(ConnectionPool connectionPool, Address address, Object callStackTrace) {
routeSelector = new RouteSelector(address, routeDatabase()
通过上面的代码可以发现RouteSelector对象的初始化需要Address和RouteDatabase这两个对象,Address这个对象是神马时候初始化的呢?在我们使用OKhttp进行访问的时候需要是需要创建Request对象的,而Request在通过Request.Builder的构建中有这么一个重载方法:

public Builder url(String url) {
....
HttpUrl parsed = HttpUrl.parse(url);

通过Request.Builder的url我们得到一个HttpUrl,StreamAllocation对象持有一个Address的引用address,而在拦截器RetryAndFollowUpInterceptor在执行intercept时候初始化了StreamAllocation对象,并且调用了RetryAndFollowUpInterceptor的createAddress(HttpUrl )方法创建一个Address对象作为StreamAllocation 对象中address值,最终这个address值交给了RouteSelector!代码如下:

//RetryAndFollowUpInterceptor拦截器的intercept方法
public Response intercept(Chain chain) {

streamAllocation = new StreamAllocation(
client.connectionPool(), createAddress(request.url()), callStackTrace);
}
//创建Address 对象交给streamAllocation.address引用
private Address createAddress(HttpUrl url) {
//省略了部分代码
return new Address(url.host(), url.port(), client.dns(), client.socketFactory(),
sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(),
client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector());
Address的构建简单分析
通过Address构造器的分析我们可以发现一个url构成的Address对象有:主机名host、端口号port、Dns、代理服务器proxy等元素(其他元素比如hostnameVerifier没有说明是因为与本博文内容无关)。

Dns:是一个接口,如果OkhttpClient在使用中没有配置自己的DNS实现则使用Okttp默认DNS.SYSTEM对象:

Dns SYSTEM = new Dns() {
@Override
public List<InetAddress> lookup(String hostname) {
return Arrays.asList(InetAddress.getAllByName(hostname));

该接口实现也很简单,就是调用了根据url中的主机名hostname通过getAllByName**获取InetAddress对象的集合*。因为有些计算机会有多个Internet地址,getAllByName方法包含所有对应此主机名的地址,通常有多个ip地址的主机大多数都是有着非常高吞吐量的web服务器。比如如果getAllByName(“www.microsoft.com”)的话,打印List 集合中InetAddress对象的toString方法你可以发现有多个如下格式的输出,格式为(hostname/ip地址)*:

www.microsoft.com/aa.aaa.aa.aaa
www.microsoft.com/xx.xxx.xx.xxx
www.microsoft.com/yy.yyy.yy.yyy

到这儿准备对RouteSelector分析的准备工作算是完成,我们看看RouteSelector构造器都做了些什么:

public RouteSelector(Address address, RouteDatabase routeDatabase) {
this.address = address;
this.routeDatabase = routeDatabase;

resetNextProxy(address.url(), address.proxy());

除了简单的为属性赋值之外,还调用了resetNextProxy方法,该方法的主要作用就是初始化RouteSelector类中的代理服务器集合proxies:

private List<Proxy> proxies = Collections.emptyList();
private void resetNextProxy(HttpUrl url, Proxy proxy) {
if (proxy != null) {//客户端配置了自己的代理
proxies = Collections.singletonList(proxy);
} else {
//通过ProxySelector.getDefault(www.wmyl11.com/);对象来获取默认代理
List<Proxy> proxiesOrNull = address.proxySelector().select(url.uri());
proxies = proxiesOrNull != null && !proxiesOrNull.isEmpty()
? Util.immutableList(proxiesOrNull)
: Util.immutableList(Proxy.NO_PROXY);/
}
//访问proxies集合的下标,初始化为0
nextProxyIndex = 0;
客户端在配置OkttCilent对象的时候是可以通过Okhttp的Builder对象来创建自己的代理服务器,用户配置的代理服务器对象proxy会交给上文所说的Address对象,进而交给RouteSelector.当然用户如果没有配置代理服务器对象,则OhttpClinet使用ProxySelector.getDefault()这个ProxySelector对象的select方法返回默认的代理服务器集合,当然如果没有找到代理服务器,则使用Proxy.NO_PROXY,表示当前Address对网络的请求完全绕开代理服务器,直接连接远程主机!

此时RouteSelector的初始化工作算是完全搞定!那么RouteSelector是做什么的呢?又是如何做的呢?顾名思义RouteSelector的作用就是Select Route(选择路由),在OKhttp中其实也就是返回一个可用的Route对象。下面通过其next方法()具体分析之。

public Route next() throws IOException {
// 没有有多余的ip地址
if (!hasNextInetSocketAddress()) {
//如果多余的代理服务器
if (!hasNextProxy()) {
//如果没有延迟的路由
if (!hasNextPostponed()) {
throw new NoSuchElementException();
}
//获取延迟的路由
return nextPostponed();
}
//如果没有多余的ip地址并且有其他代理服务器,调用nextProxy
//获取下一个代理服务器
lastProxy = nextProxy();
}

//获取ip地址列表中下一个ip地址构成的InetSocketAddress对象
lastInetSocketAddress = nextInetSocketAddress();

//创建一个Route 对象
Route route = new Route(address, lastProxy, lastInetSocketAddress);
。。。。省略部分代码。。。
//返回一路由
return route;
上面的代码逻辑也很简单:
1、如果没有多余的ip地址(还记得上文说的InetAddress.getAllByName()吗?) 则判断是否有多余的代理服务器,如果有多余的代理服务器的话,则调用nextProxy()从上文说的proxies获取一个Proxy代理服务器对象,赋值给lastProxy !
2、调用nextInetSocketAddress方法从inetSocketAddresses这个list集合中获取一个InetSocketAddress对象,赋值给lastInetSocketAddress引用!
3、将address对象、lastProxy对象和lastInetSocketAddress组成一个Route对象并返回!

看到这儿也许你会问inetSocketAddresses这个集合是什么鬼?是神马时候初始化的?前文神马没有说明,其实这个方法集合的初始化是在nextProxy(www.wmyl15.com/)方法里,下面来分析nextProxy方法:

private Proxy nextProxy() throws IOException {
//从 proxies获取一个代理服务器
Proxy result = proxies.get(nextProxyIndex++);
//重置inetSocketAddresses集合
resetNextInetSocketAddress(result);
return result;
继续看看resetNextInetSocketAddress方法:

private void resetNextInetSocketAddress(Proxy proxy) throws IOException {

//初始化InternetSocketAdds集合
inetSocketAddresses = new ArrayList<>();
//主机名
String socketHost;
//端口号
int socketPort;
//如果不使用代理或者使用SOCKS的代理服务器
if (proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.SOCKS) {
socketHost = address.url().host();//获取主机
socketPort = address.url().port();//获取端口号
} else {//如果HTPP代理服务器
//获取SocketAddress对象
SocketAddress proxyAddress = proxy.address();
。。。。
//转换成InetSocketAddress 对象
InetSocketAddress proxySocketAddress = (InetSocketAddress) proxyAddress;
//获取代理服务器的主机名和端口号
socketHost = getHostString(proxySocketAddress);
socketPort = proxySocketAddress.getPort();
}
。。。。。
//如果是socks代理,直接放入集合
if (proxy.type() == Proxy.www.longboshyl.cn/ Type.SOCKS) {
inetSocketAddresses.add(InetSocketAddress.createUnresolved(socketHost, socketPort));
} else {//如果是http代理或者是无代理连接

//获取ip地址列表
List<InetAddress> addresses = address.dns().lookup(socketHost);
。。。。。
//根据ip列表地址创建多个InetSocketAddress
for (int i = 0, size = addresses.size(); i < size; i++) {
InetAddress inetAddress = addresses.get(i);
inetSocketAddresses.add(new InetSocketAddress(inetAddress, socketPort));
}
}

//集合索引重置为0
nextInetSocketAddressIndex = 0;
可以看出resetNextInetSocketAddress方法也很简单
1、根据proxies选中的Proxy代理服务器来创建nextInetSocketAddress集合。(也就是说如果有下一个代理的话,上一个代理服务器的InteSocketAddress数据会设置空,毕竟nextInetSocketAddress集合重新初始化了)
2、根据Proxy的类型来获取服务器主机的主机名和端口号,如果没有使用代理或者是使用了SOCKS代理服务器,则根据url直接获取主机名和端口号!如果使用了HTTP的代理,则获取代理服务器的地址(SocketAddress),然后通过InetSocketAddress 获取代理服务器的主机名和端口号!
3、得到主机名socketHost 和端口号socketPort 之后如果是SOCKETS代理服务器,则通过这两个信息创建一个InetSocketAddress对象添加到集合里。如果使用的是Http代理服务器或者没有使用代理服务器,则通过上文所说的DNS根据得到的主机名socketHost 获取ip地址列表(List《InetAddress 》集合)来为每一个ip组成的InetAddress对象,构建一个InetSocketAddress,并添加到inetSocketAddresses中去!

到此为止RouteSelector的分析就介绍完毕,综合上面的讲解可以知道RouteSelector的主要作用有两个:
1、选择代理服务器Proxy
2、对有多个ip地址的(代理)服务器进行ip选择(只不过该ip地址用InetSocketAddress表示而已)
3、将选中的(代理)服务器和ip地址(InetSocketAd www.lieqibiji.com dress),及上文所说的Address构成Route对象并返回该对象。

RouteSelector的初始化的更多相关文章

  1. Okhttp之RouteSelector简单解析

    继前面的几篇OKhttp的拦截器简单分析之后,对于后续Okhttp之间的分析自己也着实琢磨了一段时间,是分析RealConnection?还是ConnectionPool,随着对Okhttp源码的深入 ...

  2. Java初始化过程

    这篇文章主要讲解Java在创建对象的时候,初始化的顺序.主要从以下几个例子中讲解: 继承关系中初始化顺序 初始化块与构造器的顺序 已经加载过的类的初始化顺序 加载父类,会不会加载子类 创建子类对象会不 ...

  3. nginx源码分析之模块初始化

    在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要 ...

  4. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  5. Java类变量和成员变量初始化过程

    一.类的初始化 对于类的初始化:类的初始化一般只初始化一次,类的初始化主要是初始化静态成员变量. 类的编译决定了类的初始化过程. 编译器生成的class文件主要对定义在源文件中的类进行了如下的更改: ...

  6. Git学习笔记一:新建本地仓库及初始化

    1.百度搜索Git下载安装,直接按默认选项安装即可. 例如:Git-2.7.2-32-bit_setup.1457942412.exe 2.配置Git信息,建立版本仓库 (Alt+PrintScerr ...

  7. Spring MVC初始化参数绑定

    初始化参数绑定与类型转换很类似,初始化绑定时,主要是参数类型 ---单日期 在处理器类中配置绑定方法  使用@InitBinder注解 在这里首先注册一个用户编辑器 参数一为目标类型   proper ...

  8. SpringMVC初始化参数绑定--日期格式

    一.初始化参数绑定[一种日期格式] 配置步骤: ①:在applicationcontext.xml中只需要配置一个包扫描器即可 <!-- 包扫描器 --> <context:comp ...

  9. SpringMvc中初始化参数绑定

    初始化参数绑定与类型转换很类似,初始化绑定时,主要是参数类型 ---单日期 在处理器类中配置绑定方法  使用@InitBinder注解 在这里首先注册一个用户编辑器 参数一为目标类型   proper ...

随机推荐

  1. 杂项-IIS:发布杂项

    ylbtech-杂项-IIS:发布杂项 1. 测试连接返回顶部 1.1.授权 无法验证对路径的访问. 1.2.详情信息 服务器配置为将传递身份验证和内置帐户一起使用,以访问指定的物理路径.但是,IIS ...

  2. iOS消息转发机制

    iOS消息转发机制 “消息派发系统”(message-dispatch system) 若想令类能够理解某条消息,我们必须实现出对应的方法才行.但是,在编译器向类发送其无法解读的消息时并不会报错,因为 ...

  3. 【render】partial及其局部变量

    原文:http://www.cnblogs.com/lwm-1988/archive/2011/09/13/2175041.html 1. partial 1.1 把partial作为view的一部分 ...

  4. xcode中用pods管理第三方库<转>

    安装pods :http://www.cnblogs.com/wangluochong/p/5567082.html 史上最详细的CocoaPods安装教程 --------------------- ...

  5. Android排错: has leaked window com.android.internal.policy.impl.PhoneWindow$ that was originally added here

    异常场景: 经常在应用中需要处理一些耗时的工作,诸如读取大文件.访问网络资源等.为了避免因程序假死而带来的糟糕用户体验,通常我们可以通过线程+Handler或者Android提供的AsyncTask来 ...

  6. 每天一道算法题目(18)——取等长有序数组的上中位数和不等长有序数组的第k小的数

    1.取上中位数 题目: 给定两个有序数组arr1和arr2,两个数组长度都为N,求两个数组中所有数的上中位数.要求:时间复杂度O(logN).      例如:          arr1 = {1, ...

  7. CSS制作水平垂直居中对齐 多种方式各有千秋

    作为前端攻城师,在制作Web页面时都有碰到CSS制作水平垂直居中,我想大家都有研究过或者写过,特别的其中的垂直居中,更是让人烦恼.这段时间,我收 集了几种不同的方式制作垂直居中方法,但每种方法各有千秋 ...

  8. 百度Apollo解析——1.总介绍

    1. 概括 Apollo源码主要是c++实现的,也有少量python,主要程序在apollo/modules目录中,共18个包,功能包17个: 其中每个模块的作用如下: apollo/modules/ ...

  9. ROS Learning-028 (提高篇-006 A Mobile Base-04) 控制移动平台 --- (Python编程)控制虚拟机器人的移动(不精确的制定目标位置)

    ROS 提高篇 之 A Mobile Base-04 - 控制移动平台 - (Python编程)控制虚拟机器人的移动(不精确的制定目标位置) 我使用的虚拟机软件:VMware Workstation ...

  10. JavaPersistenceWithHibernate第二版笔记-第七章-003Mapping an identifier bag(@OrderColumn、@ElementCollection、@CollectionTable、、)

    一.结构 二.代码 1. package org.jpwh.model.collections.listofstrings; import org.jpwh.model.Constants; impo ...