DNS 充当地址簿。它将人类可读的域名 (google.com) 转换为机器可读的 IP 地址 (142.251.46.238)。
开局一张图
来自:https://xiaolishen.medium.com/the-dns-lookup-journey-240e9a5d345c
寻址流程
- 查询浏览器缓存:当你输入一个域名后,浏览器会首先检查自己的缓存,看是否已经解析过该域名的IP地址。
- 查询操作系统的DNS缓存:如果在浏览器缓存中找不到对应的IP地址,浏览器会向操作系统发出DNS解析请求。操作系统会检查自己的DNS缓存,看是否已经解析过该域名。
- 查询OS的hosts文件:如果在操作系统的DNS缓存中找不到对应的IP地址,操作系统会检查自己的hosts文件,看是否有该域名的映射信息。hosts文件是一个本地的文本文件,可以手动添加域名和IP地址的映射关系。
- 查询家庭路由器缓存:如果在操作系统的hosts文件中找不到对应的IP地址,操作系统会将DNS解析请求发送给家庭路由器。家庭路由器也有一个DNS缓存,用于存储最近解析过的域名和IP地址。
- 查询ISP的DNS服务器:如果在家庭路由器的缓存中找不到对应的IP地址,家庭路由器会将DNS解析请求发送给配置的ISP的DNS服务器。
- 查询ISP缓存:ISP的DNS服务器收到解析请求后,会检查自己的DNS缓存,看是否已经解析过该域名。
- DNS服务器递归查询:如果在ISP的缓存中找不到对应的IP地址,ISP的DNS服务器会根据域名的层级结构,从根域名服务器开始递归查询,直到找到负责该域名的权威域名服务器。(这里也有可能是迭代查询 )
- 解析器向根名称服务器询问域名,假设它是example.com
- 根名称服务器知道所有TLD(顶级域)名称服务器。由于我们附带了.com域,因此它将我们的查询转发到处理.com域的 TLD 名称服务器。
- .com TLD 名称服务器知道存储example.com域的 DNS 记录的权威名称服务器,因此它会提前转发查询。
- 权威名称服务器响应映射到域名的A 记录(地址记录,即 IP 地址)。
- 然后,IP 地址一路传回我们的浏览器,中间每个拥有 DNS 缓存的浏览器都会在途中缓存它,因此下次当我们或其他人询问 example.com 时,会更快的响应。
- 浏览器打开与 IP 地址(即托管example.com的服务器的地址)的 TCP/IP 连接,然后发送 HTTP 请求。如果服务器已启动并正在运行,它会将 HTTP 响应发送回我们的浏览器。
DNS 查询类型
递归查询 - 客户端向 ISP DNS 服务器发送请求后,它必须响应客户端请求的答案,即: www.example.com
的 IP 地址。如果ISP的DNS服务器没有答案,它必须不断地自行寻找答案,直到得到答案,然后它回复客户端。
迭代查询 - 当ISP DNS服务器向远程DNS服务器发送请求后,远程DNS服务器不需要响应应答,它可以响应另一个DNS服务器的IP地址来进行引用。 ISP DNS 服务器查看响应并向另一台服务器重新发出新请求。它不断重复这个过程,直到找到答案。
DNS zone
DNS缓存
-
浏览器DNS缓存
在 Chrome 浏览器中,您可以转到 chrome://net-internals/#dns 查看 DNS 缓存的状态。
-
本地DNS缓存
在 Windows 中查看 DNS 缓存条目
ipconfig /displaydns
刷新 DNS 缓存
ipconfig /flushdns
并非所有 Linux 发行版都启用或默认安装 DNS 缓存
Debian 或 Ubuntu 系统,则 DNS 缓存文件存储在目录中 /var/cache/nscd/hosts
sudo strings /var/cache/nscd/hosts
-
hosts文件
-
路由器DNS缓存
-
ISP(运营商)DNS缓存
-
Java DNS Cache
高级
Java DNS Cache
相关代码在InetAddressCachePolicy.java
public final class InetAddressCachePolicy {
// 成功寻址的缓存策略
// 安全属性
private static final String cachePolicyProp = "networkaddress.cache.ttl";
// 系统属性
private static final String cachePolicyPropFallback =
"sun.net.inetaddr.ttl";
// 失败寻址的无效缓存策略
// 安全属性
private static final String negativeCachePolicyProp =
"networkaddress.cache.negative.ttl";
// 系统属性
private static final String negativeCachePolicyPropFallback =
"sun.net.inetaddr.negative.ttl";
public static final int FOREVER = -1;
public static final int NEVER = 0;
/* default value for positive lookups */
public static final int DEFAULT_POSITIVE = 30;
解析成功缓存策略
Integer tmp = java.security.AccessController.doPrivileged(
new PrivilegedAction<Integer>() {
public Integer run() {
// 读取参数 networkaddress.cache.ttl 作为缓存 ttl
try {
String tmpString = Security.getProperty(cachePolicyProp);
if (tmpString != null) {
return Integer.valueOf(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
// 读取启动参数 sun.net.inetaddr.ttl 作为缓存 ttl
try {
String tmpString = System.getProperty(cachePolicyPropFallback);
if (tmpString != null) {
return Integer.decode(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
return null;
}
});
if (tmp != null) {
cachePolicy = tmp < 0 ? FOREVER : tmp;
propertySet = true;
} else {
// 如果以上参数均未读取到,并且未开启 SecurityManager,则会使用默认值 30s.
if (System.getSecurityManager() == null) {
cachePolicy = DEFAULT_POSITIVE;
}
// 否则使用初始值 -1,表示永久生效
}
- 如果开启了 SecurityManager,优先从 ${java.home}/jre/lib/security/java.security 中读取参数 networkaddress.cache.ttl,单位为秒。
- 如果未读取到,则会读取启动参数 sun.net.inetaddr.ttl 作为缓存 ttl.
- 如果以上参数均未读取到,并且未开启 SecurityManager,则会使用默认值 30s.(默认是未启用,即默认是30s有效期)
- 否则使用初始值 -1,表示永久生效。
SecurityManager
是一个允许应用实现一种安全策略的类。应用在执行一个安全或敏感的操作之前,可以明确此操作是否在一个安全的上下文中被执行。默认是未启用状态
1.系统启动开启
JVM参数:
-Djava.security.manager
2.程序手动开启
// 创建SecurityManager实例 SecurityManager securityManager = new SecurityManager(); // 设置启动 System.setSecurityManager(securityManager);
寻址失败无效的DNS缓存时间
tmp = java.security.AccessController.doPrivileged (
new PrivilegedAction<Integer>() {
public Integer run() {
// 读取 networkaddress.cache.negative.ttl 默认10s
try {
String tmpString = Security.getProperty(negativeCachePolicyProp);
if (tmpString != null) {
return Integer.valueOf(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
// 读取 sun.net.inetaddr.negative.ttl
try {
String tmpString = System.getProperty(negativeCachePolicyPropFallback);
if (tmpString != null) {
return Integer.decode(tmpString);
}
} catch (NumberFormatException ignored) {
// Ignore
}
return null;
}
});
if (tmp != null) {
negativeCachePolicy = tmp < 0 ? FOREVER : tmp;
propertyNegativeSet = true;
}
当DNS解析无效的时候,默认10S,目的是为了帮助提升性能
总结
可查看源码InetAddress 和 InetAddressCachePolicy
java dns
解析缓存之源码解析- 如果打开
Java
中的SecurityManager
,DNS
缓存将不会失效。 - 否则,可访问的
DNS
解析缺省缓存30秒,不可访问的DNS
解析缺省缓存10秒。
- 如果打开
- 关于
jvm dns cache
(域名缓存时间),给出“对于多条A记录是采用什么策略返回IP的结论:- 在缓存有效期内,取到的
IP
永远是缓存中全部A记录的第一条,并没有轮循之类的策略。 - 缓存失效之后重新进行DNS解析,因为每次域名解析返回的A记录顺序会发生变化(
nslookup www.google.com
测试可见),所以缓存中的数据顺序也变了,取到的IP
也变化。
- 在缓存有效期内,取到的
如何设置 JVM TTL
要修改 JVM 的 TTL,请设置 networkaddress.cache.ttl 属性值。根据您的需求,使用下列方法之一:
-
全局 (针对所有使用 JVM 的应用程序)。
networkaddress.cache.ttl
在 Java 8$JAVA_HOME/jre/lib/security/java.security
的文件或适用于 Java 11 或更高版本$JAVA_HOME/conf/security/java.security
的文件中设置:networkaddress.cache.ttl=60
-
仅针对应用程序,在应用程序的初始化代码中设置
networkaddress.cache.ttl
:java.security.Security.setProperty("networkaddress.cache.ttl" , "60");