Okhttp之RouteSelector简单解析
来源:互联网 发布:java web项目搭建教程 编辑:程序博客网 时间:2024/05/22 17:38
继前面的几篇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.aaawww.microsoft.com/xx.xxx.xx.xxxwww.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();对象来获取默认代理 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()方法里,下面来分析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.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地址(InetSocketAddress),及上文所说的Address构成Route对象并返回该对象。
可以用图简单来表示一下:
其实如果读过《Http权威指南》这本书的话,这个类其实有点**类似于**DNS重定向功能的功能,简单的决定一个host的ip列表中选中哪一个ip作为目标主机地址来使用:
- Okhttp之RouteSelector简单解析
- OKHTTP简单解析
- Android Okhttp之Okio解析
- OkHttp源码解析之概述
- OkHttp之BridgeInterceptor简单分析
- Okhttp之CacheInterceptor简单分析
- OkHttp之ConnectInterceptor简单分析
- Okhttp之CallServerInterceptor简单分析
- Okhttp源码简单解析(一)
- Android Okhttp+Retrofit2.0+RxJava简单解析
- OKhttp源码解析---拦截器之RetryAndFollowUpInterceptor
- OKhttp源码解析---拦截器之BridgeInterceptor
- OKhttp源码解析---拦截器之CacheInterceptor
- OKhttp源码解析---拦截器之ConnectInterceptor
- OKhttp源码解析---拦截器之CallServerInterceptor
- Okhttp源码解析之Interceptor(拦截器)
- Android okHttp网络请求之Json解析
- OkHttp 3.x 源码解析之Dispatcher
- PAT乙级 1048. 数字加密(20)
- QT中窗体之间传值--signals&slots
- Mac 安装java 环境变量 和android环境遇到的问题 JRE System Library(unbound)系统JRE未绑定解决方案
- 理解Handler消息机制
- MyEclipse关闭某类或某个具体文件的验证的方法
- Okhttp之RouteSelector简单解析
- 动态规划总结
- 如何解决error C4996: 'fopen'的问题
- 十七、单例模式——设计模式学习笔记
- PAT乙级 1049. 数列的片段和(20)
- caffe中 cifar10案例(一)训练模型
- 机器学习笔记-第二章 多变量线性回归
- 关于项目中web.xml配置文件里面的spring的配置contextConfigLocation
- 一致性hash