图解系列 DNS查找过程和DNS缓存

DNS 充当地址簿。它将人类可读的域名 (google.com) 转换为机器可读的 IP 地址 (142.251.46.238)。

开局一张图

img

来自:https://xiaolishen.medium.com/the-dns-lookup-journey-240e9a5d345c

寻址流程

  1. 查询浏览器缓存:当你输入一个域名后,浏览器会首先检查自己的缓存,看是否已经解析过该域名的IP地址。
  2. 查询操作系统的DNS缓存:如果在浏览器缓存中找不到对应的IP地址,浏览器会向操作系统发出DNS解析请求。操作系统会检查自己的DNS缓存,看是否已经解析过该域名。
  3. 查询OS的hosts文件:如果在操作系统的DNS缓存中找不到对应的IP地址,操作系统会检查自己的hosts文件,看是否有该域名的映射信息。hosts文件是一个本地的文本文件,可以手动添加域名和IP地址的映射关系。
  4. 查询家庭路由器缓存:如果在操作系统的hosts文件中找不到对应的IP地址,操作系统会将DNS解析请求发送给家庭路由器。家庭路由器也有一个DNS缓存,用于存储最近解析过的域名和IP地址。
  5. 查询ISP的DNS服务器:如果在家庭路由器的缓存中找不到对应的IP地址,家庭路由器会将DNS解析请求发送给配置的ISP的DNS服务器。
  6. 查询ISP缓存:ISP的DNS服务器收到解析请求后,会检查自己的DNS缓存,看是否已经解析过该域名。
  7. DNS服务器递归查询:如果在ISP的缓存中找不到对应的IP地址,ISP的DNS服务器会根据域名的层级结构,从根域名服务器开始递归查询,直到找到负责该域名的权威域名服务器。(这里也有可能是迭代查询
    1. 解析器向根名称服务器询问域名,假设它是example.com
    2. 根名称服务器知道所有TLD(顶级域)名称服务器。由于我们附带了.com域,因此它将我们的查询转发到处理.com域的 TLD 名称服务器。
    3. .com TLD 名称服务器知道存储example.com域的 DNS 记录的权威名称服务器,因此它会提前转发查询。
    4. 权威名称服务器响应映射到域名的A 记录(地址记录,即 IP 地址)。
  8. 然后,IP 地址一路传回我们的浏览器,中间每个拥有 DNS 缓存的浏览器都会在途中缓存它,因此下次当我们或其他人询问 example.com 时,会更快的响应。
  9. 浏览器打开与 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

img

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,表示永久生效
        }
  1. 如果开启了 SecurityManager,优先从 ${java.home}/jre/lib/security/java.security 中读取参数 networkaddress.cache.ttl,单位为秒。
  2. 如果未读取到,则会读取启动参数 sun.net.inetaddr.ttl 作为缓存 ttl.
  3. 如果以上参数均未读取到,并且未开启 SecurityManager,则会使用默认值 30s.(默认是未启用,即默认是30s有效期
  4. 否则使用初始值 -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中的SecurityManagerDNS缓存将不会失效。
    • 否则,可访问的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");
    

猜你喜欢

转载自blog.csdn.net/abu935009066/article/details/132062189