今天再说说网络,大家知道网络访问的第一步就是解析域名,也就是常说的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接口即可,简单写个例子:
//导入库
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