DNS域名解析三问

2020-11-09 15:10:03 浏览数 (2)

今天再说说网络,大家知道网络访问的第一步就是解析域名,也就是常说的DNS解析,那么你对DNS又了解多少呢?来看看吧:

  • 说说DNS,以及存在的问题
  • 怎么优化DNS解析
  • DNS解析超时怎么办

说说DNS,以及存在的问题

之前看过我说的网络问题应该知道DNS用来做域名解析工作的,当输入一个域名后,需要把域名转化为IP地址,这个转换过程就是DNS解析

但是传统的DSN解析会有一些问题,比如:

  • 域名缓存问题本地做一个缓存,直接返回缓存数据。可能会导致全局负载均衡失败,因为上次进行的缓存,不一定是这次离客户最近的地方,可能会绕远路。
  • 域名转发问题如果是A运营商将解析的请求转发给B运营商,B去权威DNS服务器查询的话,权威服务器会认为你是B运营商的,就返回了B运营商的网站地址,结果每次都会跨运营商。
  • 出口NAT问题做了网络地址转化后,权威的DNS服务器,没法通过地址来判断客户到底是哪个运营商,极有可能误判运营商,导致跨运营商访问。
  • 域名更新问题本地DNS服务器是由不同地区,不同运营商独立部署的,对域名解析缓存的处理上,有区别,有的会偷懒忽略解析结果TTL的时间限制,导致服务器没有更新新的ip而是指向旧的ip。
  • 解析延迟DNS的查询过程需要递归遍历多个DNS服务器,才能获得最终结果。可能会带来一定的延时。
  • 域名劫持DNS域名解析服务器有可能会被劫持,或者被伪造,那么正常的访问就会被解析到错误的地址。
  • 不可靠由于DNS解析是运行在UDP协议之上的,而UDP我之前也说过是一种不可靠的协议,他的优势在于实时性,但是有丢包的可能。

这些问题不仅会让访问速度变慢,还有可能会导致访问异常,访问页面被替换等等。

怎么优化DNS解析

  • 安全优化

总之DNS还是会有各种问题吧,怎么解决呢?就是用HTTPDNS

HTTPDNS是一个新概念,他会绕过传统的运营商DNS服务器,不走传统的DNS解析。而是换成HTTP协议,直接通过HTTP协议进行请求某个DNS服务器集群,获取地址。

  • 由于绕过了运营商,所以可以避免域名被劫持。
  • 它是基于访问的来源ip,所以能获得更准确的解析结果
  • 会有预解析解析缓存等功能,所以解析延迟也很小

所以首先的优化,针对安全方面,就是要替换成HTTPDNS解析方式,就要借用阿里云和腾讯云等服务,但是这些服务可不是免费的,有没有免费的呢?有的,七牛云的 happy-dns。添加依赖库,然后去实现okhttp的DNS接口即可,简单写个例子:

代码语言:javascript复制

//导入库
    implementation 'com.qiniu:happy-dns:0.2.13'
    implementation 'com.qiniu.pili:pili-android-qos:0.8'


//实现DNS接口
public class HttpDns implements Dns {

    private DnsManager dnsManager;

    public HttpDns() {
        IResolver[] resolvers = new IResolver[1];
        try {
            resolvers[0] = new Resolver(InetAddress.getByName("119.29.29.29"));
            dnsManager = new DnsManager(NetworkInfo.normal, resolvers);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<InetAddress> lookup(String hostname) throws UnknownHostException {
        if (dnsManager == null)  //当构造失败时使用默认解析方式
            return Dns.SYSTEM.lookup(hostname);

        try {
            String[] ips = dnsManager.query(hostname);  //获取HttpDNS解析结果
            if (ips == null || ips.length == 0) {
                return Dns.SYSTEM.lookup(hostname);
            }

            List<InetAddress> result = new ArrayList<>();
            for (String ip : ips) {  //将ip地址数组转换成所需要的对象列表
                result.addAll(Arrays.asList(InetAddress.getAllByName(ip)));
            }
            //在返回result之前,我们可以添加一些其他自己知道的IP
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        //当有异常发生时,使用默认解析
        return Dns.SYSTEM.lookup(hostname);
    }
}


//替换okhttp的dns解析
OkHttpClient okHttpClient = new OkHttpClient.Builder().dns(new HttpDns()).build();

  • 速度优化

如果在测试环境,其实我们可以直接配置ip白名单,然后跳过DNS解析流程,直接获取ip地址。比如:

代码语言:javascript复制
    private static class TestDNS implements Dns{
        @Override
        public List<InetAddress> lookup(@NotNull String hostname) throws UnknownHostException {
            if ("www.test.com".equalsIgnoreCase(hostname)){
                InetAddress byAddress=InetAddress.getByAddress(hostname,new byte[]{(byte)192,(byte)168,1,1});
                return Collections.singletonList(byAddress);
            }else {
                return Dns.SYSTEM.lookup(hostname);
            }
        }
    }

DNS解析超时怎么办

当我们在用OKHttp做网络请求时,如果网络设备切换路由,访问网络出现长时间无响应,很久之后会抛出 UnknownHostException。虽然我们在OkHttp中设置了connectTimeout超时时间,但是它其实对DNS的解析是不起作用的。

这种情况我们就需要在自定义的Dns类中做超时判断:

代码语言:javascript复制
public class TimeDns implements Dns {
    private long timeout;

    public TimeDns(long timeout) {
        this.timeout = timeout;
    }

    @Override
    public List<InetAddress> lookup(final String hostname) throws UnknownHostException {
        if (hostname == null) {
            throw new UnknownHostException("hostname == null");
        } else {
            try {
                FutureTask<List<InetAddress>> task = new FutureTask<>(
                        new Callable<List<InetAddress>>() {
                            @Override
                            public List<InetAddress> call() throws Exception {
                                return Arrays.asList(InetAddress.getAllByName(hostname));
                            }
                        });
                new Thread(task).start();
                return task.get(timeout, TimeUnit.MILLISECONDS);
            } catch (Exception var4) {
                UnknownHostException unknownHostException =
                        new UnknownHostException("Broken system behaviour for dns lookup of "   hostname);
                unknownHostException.initCause(var4);
                throw unknownHostException;
            }
        }
    }
}

//替换okhttp的dns解析
OkHttpClient okHttpClient = new OkHttpClient.Builder().dns(new TimeDns(5000)).build();

参考

https://blog.csdn.net/quwei3930921/article/details/85336552 https://kaiwu.lagou.com/course/courseInfo.htm?courseId=67#/detail/pc

0 人点赞