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的容器~

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值