解析主机名需要5秒钟


8

我有一个主bind9DNS服务器和2个在IPv4(Debian Jessie)上运行的从属服务器,使用/etc/bind/named.conf

listen-on-v6 { none; };

当我尝试从不同的服务器进行连接时,每个连接至少需要5秒钟(我使用约瑟夫的计时信息进行调试):

$ curl -w "@curl-format.txt" -o /dev/null -s https://example.com
            time_namelookup:  5.512
               time_connect:  5.512
            time_appconnect:  5.529
           time_pretransfer:  5.529
              time_redirect:  0.000
         time_starttransfer:  5.531
                            ----------
                 time_total:  5.531

根据curl,查找通常花费大部分时间,但是标准nslookup非常快:

$ time nslookup example.com > /dev/null 2>&1

real    0m0.018s
user    0m0.016s
sys     0m0.000s

强制curl使用IPv4之后,它会变得更好:

$ curl -4 -w "@curl-format.txt" -o /dev/null -s https://example.com

            time_namelookup:  0.004
               time_connect:  0.005
            time_appconnect:  0.020
           time_pretransfer:  0.020
              time_redirect:  0.000
         time_starttransfer:  0.022
                            ----------
                 time_total:  0.022

我已在主机上禁用IPv6:

echo 1 > /proc/sys/net/ipv6/conf/eth0/disable_ipv6

虽然问题仍然存在。我尝试运行strace以查看超时的原因:

write(2, "*", 1*)                        = 1
write(2, " ", 1 )                        = 1
write(2, "Hostname was NOT found in DNS ca"..., 36Hostname was NOT found in DNS cache
) = 36
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 4
close(4)                                = 0
mmap(NULL, 8392704, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7f220bcf8000
mprotect(0x7f220bcf8000, 4096, PROT_NONE) = 0
clone(child_stack=0x7f220c4f7fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f220c4f89d0, tls=0x7f220c4f8700, child_tidptr=0x7f220c4f89d0) = 2004
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 4)                           = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 8)                           = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 16)                          = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 32)                          = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7f22102e08d0}, NULL, 8) = 0
poll(0, 0, 64)                          = 0 (Timeout)

由于nslookup(或curl -4)正在使用相同的DNS服务器,这似乎不是防火墙问题。知道有什么问题吗?

下面是tcpdump从主机tcpdump -vvv -s 0 -l -n port 53

tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:14:52.542526 IP (tos 0x0, ttl 64, id 35839, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x96c7!] 39535+ A? example.com. (35)
20:14:52.542540 IP (tos 0x0, ttl 64, id 35840, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x6289!] 45997+ AAAA? example.com. (35)
20:14:52.543281 IP (tos 0x0, ttl 61, id 63674, offset 0, flags [none], proto UDP (17), length 158)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 45997* q: AAAA? example.com. 1/1/0 example.com. [1h] CNAME s01.example.com. ns: example.com. [10m] SOA ns01.example.com. ns51.domaincontrol.com. 2016062008 28800 7200 1209600 600 (130)
20:14:57.547439 IP (tos 0x0, ttl 64, id 36868, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x96c7!] 39535+ A? example.com. (35)
20:14:57.548188 IP (tos 0x0, ttl 61, id 64567, offset 0, flags [none], proto UDP (17), length 184)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 39535* q: A? example.com. 2/2/2 example.com. [1h] CNAME s01.example.com., s01.example.com. [1h] A 136.243.154.168 ns: example.com. [30m] NS ns01.example.com., example.com. [30m] NS ns02.example.com. ar: ns01.example.com. [1h] A 136.243.154.168, ns02.example.com. [1h] A 192.168.1.2 (156)
20:14:57.548250 IP (tos 0x0, ttl 64, id 36869, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.1.1.59163 > 192.168.1.2.53: [bad udp cksum 0xf9f3 -> 0x6289!] 45997+ AAAA? example.com. (35)
20:14:57.548934 IP (tos 0x0, ttl 61, id 64568, offset 0, flags [none], proto UDP (17), length 158)
    192.168.1.2.53 > 192.168.1.1.59163: [udp sum ok] 45997* q: AAAA? example.com. 1/1/0 example.com. [1h] CNAME s01.example.com. ns: example.com. [10m] SOA ns01.example.com. ns51.domaincontrol.com. 2016062008 28800 7200 1209600 600 (130)

编辑: 在绑定日志中经常出现此消息:

error sending response: host unreachable

不过,每个查询最终都会得到回答(只需5秒钟)。所有机器都是物理服务器(这不是NAT的问题),数据包更有可能被路由器阻止。这是一个很可能相关的问题:DNS查找有时需要5秒钟


1
strace -tt在跟踪延迟时,将使跟踪更有意义。
JigglyNaga

谢谢,在这种情况下没有太大帮助。在尝试通过增加超时重试相同的连接时,它似乎停留在外观中poll(0, 0, 1000) = 0 (Timeout)。在DNS服务器端,我经常遇到错误error sending response: host unreachable,好像传出数据包被阻止了(但不是nslookup)。
Tombart

Answers:


9

简短答案:

一种解决方法是,通过向以下行添加一行来强制glibc重用套接字以查找AAAAA记录/etc/resolv.conf

options single-request-reopen

此问题的真正原因可能是:

长答案:

诸如此类的程序curlwget使用glibc的函数getaddrinfo(),该函数通过并行查找两个DNS记录来尝试与IPv4和IPv6兼容。在接收到两个记录之前,它不会返回结果(与这种行为相关的几个问题)- strace上面已经解释了。强制使用IPv4时,例如curl -4内部gethostbyname()查询A仅用于记录。

tcpdump我们可以看到:

  • -> A? 开始时发送两个请求
  • -> AAAA? (请求IPv6地址)
  • <- AAAA 回复
  • -> A? 再次请求IPv4地址
  • <- A 得到了答复
  • -> AAAA? 再次请求IPv6
  • <- AAAA 回复

一个A答复由于某种原因而被丢弃,这是此错误消息:

error sending response: host unreachable

但是我不清楚为什么需要第二次AAAA查询。

要验证您是否遇到相同的问题,可以在中更新超时/etc/resolv.conf

options timeout:3

如此处所述

$ curl -w "@curl-format.txt" -o /dev/null -s https://example.com

            time_namelookup:  3.511
               time_connect:  3.511
            time_appconnect:  3.528
           time_pretransfer:  3.528
              time_redirect:  0.000
         time_starttransfer:  3.531
                            ----------
                 time_total:  3.531

中还有两个其他相关选项man resolv.conf

单一请求(自glibc 2.10起)RES_SNGLKUP 在中 设置_res.options。默认情况下,从2.9版开始,glibc并行执行IPv4和IPv6查找。某些设备DNS服务器无法正确处理这些查询,并使请求超时。此选项禁用该行为,并使glibc顺序执行IPv6和IPv4请求(以解决过程变慢的代价)。

single-request-reopen(自glibc 2.9起) 解析程序对A和AAAA请求使用相同的套接字。某些硬件错误地仅发送回一个答复。发生这种情况时,客户端系统将坐下来等待第二次答复。启用此选项将更改此行为,以便如果未正确处理来自同一端口的两个请求,它将在发送第二个请求之前关闭套接字并打开一个新请求。

相关问题:


@RuiFRibeiro仅仅基于升压,用户似乎发现其他答案更有帮助。没有冒犯的意思。
Tombart

4

正如@Tombart所说的,延迟是由于等待IPv6解析超时所致。

另一个可行的方法是在/etc/gai.conf中为IPv4赋予优先级

来自/etc/gai.conf中的注释

#   For sites which prefer IPv4 connections change the last line to
#
precedence ::ffff:0:0/96  100

更改后gai.conf,您需要使用DNS解析器库重新启动任何应用程序,以使更改生效。

请注意,如果您使用的是没有IPv6连接的BIND服务器,建议您named在根提示IPv6地址中禁用IPv6 并从中获取IPv6地址。显然,它仍将尝试解析AAAA地址。

因此,对于BIND配置,

在/ etc / default / bind9中,为IPv4地址添加-4:

OPTIONS="-4 -u bind"

在中/etc/bind/db.root,删除所有具有AAAA DNS根目录的行。


2

使用BIND9时遇到类似的问题。为了解决这个问题,我需要添加:

filter-aaaa-on-v4 yes;

我的选择named.conf

更多信息

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.