OkHttp其实暴露了一个Dns接口,默认的实现是使用系统的方法发送udp请求进行dns解析。于是,我们就可以实现一个Dns接口,解析的方式使用httpdns,将解析结果返回,接口实现之后将系统默认的Dns接口替换成我们的Dns接口。
首先,新建HttpDns类,实现Dns接口。内部维持一个系统默认的Dns对象。
public class HttpDns implements Dns {
private static final Dns SYSTEM = Dns.SYSTEM;
@Override
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
Log.e("HttpDns", "lookup:" + hostname);
return SYSTEM.lookup(hostname);
}
}
之后初始化OkHttp的时候将Dns替换为HttpDns对象。
OkHttpClient client = new OkHttpClient.Builder()
.dns(new HttpDns())
.build();
这样,使用client对象发出去的请求都是走httpdns解析dns的,除非没有命中httpdns缓存。这样做有什么好处呢?这样做相当于还是用域名进行访问,只不过底层的dns解析换成了http协议。也就是和之前系统的dns解析没有差别,但是得保证httpdns返回的ip是正确的。https下不会存在任何问题,证书校验依然使用域名进行校验
- cookie的问题也自然不存在。
同样的,解决了一部分问题后,也有一部分的风险。风险在哪呢?
- 过于底层,容灾不好做,除非强制关闭Httpdns。
- 服务器返回的ip如果不正确,这次请求就挂了,甚至下次也可能挂了。
- OkHttp默认对解析结果有一定时间的缓存,万一ttl过期了,okhttp可能依然会去使用,这时候也是有风险的。
对比两种方案,各有各的优点,一个方便做容灾,但同时也暴露出了很多问题,一个不方便做容灾,但是之前暴露出来的问题都不存在。所以,这时候就得根据实际情况衡量选择哪一种方案了。
还有就是WebView的HttpDns,目前看来Android的Webview简直就是一个Bug的存在,没有什么好的解决方法。在IOS中,Webview的请求是一个正常的HttpRequest对象,不会像Android中存在这种问题,Andorid中比较好的解决方法就是在native层的网络库里开一个代理服务器,将Webview的所有请求转发至这个代理服务器,由这个代理服务器将请求通过httpdns转换成ip请求,将请求结果返回给webview。
或者通过WebView的资源拦截接口拦截资源请求,不过这种方式只能处理资源,处理正常的http/https请求会存在问题,在Android5.0以下存在cookie种不进去的问题。
总之WebView就是蛋疼的存在,没有什么好的办法,除非有自己的Webview的容器~