Android OkHttp实现HttpDns的最佳实践(非拦截器)

之前写过一篇文章 Android 使用OkHttp支持HttpDNS,该文章中使用的是OkHttp的拦截器来实现HttpDNS。在请求发出去之前,将URL中的域名替换成ip,再往Header中添加Host。这种方式有以下优点。

  • 上层方便控制哪些请求使用了HttpDNS,可以做相应的容灾处理,比如ip请求失败时使用域名进行重试。

同样的也有很多缺点。

  • Https场景下ip直连出现的证书校验问题
  • 代理场景下的HttpDNS问题
  • ip访问的时候Cookie的问题

于是,不得不寻找一种更加的解决方案,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);
    }
}

我们只需要在lookup方法中调用HttpDns的SDK去获取IP,如果获取到的ip非空,并且ttl没有过期,则使用HttpDns。完整的方法的代码如下

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);
        String ip = DNSHelper.getIpByHost(hostname);
        if (ip != null && !ip.equals("")) {
            List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ip));
            Log.e("HttpDns", "inetAddresses:" + inetAddresses);
            return inetAddresses;
        }
        return SYSTEM.lookup(hostname);
    }
}

DNSHelper类中做的就是将域名转换为ip,具体转换过程涉及到缓存,这里不展开,可以参考新浪的HTTPDNS库https://github.com/CNSRE/HTTPDNSLib

之后初始化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的容器~

已标记关键词 清除标记
相关推荐
<span style="color:#666666;font-size:14px;background-color:#FFFFFF;">VUE是目前最火的前端框架之一,就业薪资很高,本课程教您如何快速学会VUE并应用到实战,教你如何解决内存泄漏,常用UI库的使用,自己封装组件,正式上线白屏问题,性能优化等。对正在工作当中或打算学习VUE高薪就业的你来说,那么这门课程便是你手中的葵花宝典。</span><br /> <br /> <span style="color:#666666;font-size:14px;background-color:#FFFFFF;">学习技巧:学习当中不要只看,一定要多敲代码,如果碰到某一个知识点不是很明白,不要钻牛角尖,千万不要因为一个点,放弃整个森林,接着往下学,硬着头皮开发项目只要能亲自开发一个完整的项目,你会发现不明白的地方自然而然就明白了,项目做出来就真正的学会了。</span><br /> <br /> <span style="color:#666666;font-size:14px;background-color:#FFFFFF;">此vue课程以面试和实战为基础进行讲解,每个知识点都会让你知道在实际项目开发中如何使用,学习后,可以开发大型项目,增强逻辑思维,至少让你拥有3年以上开发经验的实力!</span><br /> <br /> <span style="color:#666666;font-size:14px;background-color:#FFFFFF;">代码和ppt均可下载!</span><br /> <br /> <p> <span style="color:#666666;font-size:14px;background-color:#FFFFFF;">免费提供《企业级完整实战项目接口文档》,绝对可用。</span> </p> <p> <img src="https://img-bss.csdn.net/202001090736032736.png" alt="" /><img src="https://img-bss.csdn.net/202001090736166806.png" alt="" /><img src="https://img-bss.csdn.net/202001090736273968.png" alt="" /> </p> <p> <br /> </p> <p> <br /> </p>
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页