Slow-DNS-Lookup问题:默认启用了 ipv6-dns-lookup 以至于必须等待超时
@郑昀汇总
一. 现象:
Java HttpClient 从主站机房发起对××××的 open.×××.com 域名下 WebService 接口的 HTTP 请求,极其缓慢。
经排查:
1.1. wget 实测结果
不指定 IP协议,wget 访问它的接口,时间为 4.163 秒,基本浪费在解析主机的过程上。
如指定 wget 的 IP协议为IPV4,wget 测试则仅需 0.096 秒。
1.2. wget 对比测试:
不指定IP协议,wget 访问另一个电商的接口,时间为 0.798 秒。
1.3. dig 解析 open.×××.com:
发现在解析 open.×××.com 的时候,会 CNAME 到一个全局变量中,再由这个全局变量选择最优的线路提供访问。
二. 有可能导致 Slow-DNS-Resolution 的原因:
1)浏览器对××××的接口浏览快,不能证明什么。譬如火狐默认禁用了 ipv6 dns lookup:
2)Ubuntu 从 10.4 开始引入一个问题:操作系统默认启用了 ipv6,导致一些应用程序发出 HTTP 请求时,在 ipv4 介入之前,总是先等待 ipv6 DNS lookup 超时(常有人报告需约30秒),这也是一个 DNS lookup 过程缓慢的常见原因。
3)也有人报告:『某一天运维部宣告公司网络已经全部 enable IPv6 了,在此之后在使用 php 的 curl 函数或使用 Linux 下的 wget 访问一个内部域名时,均发现需要等待大概 5 秒才能得到结果。原因是,没有为这个域名绑定一个 IPv6 的地址,wget 时必须等 ipv6 DNS lookup 超时。现在很多服务器都开启了 IPv6 却没有路由,无法真正工作,反而导致一些不可预料的问题。』
总之,此事应该与 ipv6-dns-lookup 有关。
三. 原因:
××××在网络接入设备上做了调整,解决了问题。
原因:
1)××××使用了 F5 的 GTM 设备(GSLB,全局负载均衡),即我们常说的智能DNS。
2)它把 需要让不同区域用户访问不同IDC节点的域名 通过 F5 的 GTM 来做解析。
3)可能它的设备默认启用了 ipv6,但对于不同域名却又没有配置 ipv6 地址。
四. ipv6-dns-lookup 背景知识:
4.1. Java 平台下 ipv6 是如何工作的?
并非所有操作系统都支持 ipv6 协议,即使 Java networking stack 优先尝试检测它,并在发现可用时透明地使用它,也还可以利用系统属性禁用它。在 ipv6 不可用或被显式禁用的情况下,Inet6Address 对大多数网络连接操作都不再是有效参数。
『
第一步,Java networking stack 先确认底层操作系统是否支持 ipv6。如果支持 ipv6 ,Java 将尝试使用 ipv6 stack。
第二步,在双堆栈(dual-stack,指ipv4 stack+ipv6 stack)系统上,将创建一个 ipv6 socket。在 separate-stack 系统上,事情要复杂得多,Java 将创建两个 socket,一个给 ipv4 一个给 ipv6。
第三步,对于客户端 TCP 应用,一旦 socket 连上了,那 internet-protocol family type 就固定了,多余的那个 socket 就关闭了。对于服务器端 TCP 应用,由于不知道下一个客户端请求用什么 ip family type,所以这两个 sockets 将继续保留。对于 UDP 应用,这两个 sockets 始终都需要保留。
』
4.2. ipv6 相关的系统参数
系统有两个参数:
1)首选的协议栈:ipv4还是ipv6;
2)首选的地址族(address family type):inet4 还是 inet6。
4.2.1. 协议栈
由于在一个双堆栈系统上, ipv6 socket 能与 ipv4 和 ipv6 对端交互,所以 ipv6 stack 是默认首选项。
你可以通过如下系统参数修改配置:
java.net.preferIPv4Stack=<true|false>
对应的 java 代码是:
java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
4.2.2. 地址族
默认我们首选 ipv4 地址族。
你可以通过如下系统参数修改配置:
java.net.preferIPv6Addresses=<true|false>
对应的 java 代码是:
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");
4.3. Linux 上禁用 ipv6 的办法:
Edit /etc/sysconfig/network. (A reboot will be required)
# vi /etc/sysconfig/network
Change:
NETWORKING_IPV6=yes
To:
NETWORKING_IPV6=no
Disable IPv6 Protocol Stack for Kernel
禁止IPV6的内核模块
Edit /etc/modprobe.conf.
# vi/etc/modprobe.conf
Add the following 2 lines: (A reboot will be required)
alias net-pf-10 off
alias ipv6 off
4.4. F5 上 ipv6 相关设置:
据说,通过如下命令可以设置 BIG-IP 禁用 ipv6 :
bigpipe db Ipv6.Enabled false
bigpipe save all
BIG-IP 与 ipv6 有关的参数默认值如下所示:
Ipv6.Enabled = true
Ipv6.Nbr.DelayTime = 1
Ipv6.Nbr.IncompleteTimeout = 5
Ipv6.Nbr.MaxEntries = 2048
Ipv6.Nbr.ReachableTimeout = 30
Ipv6.Nbr.ReapTimeout = 3600
Ipv6.Nbr.Retries = 2
Ipv6.Nbr.DelayTime = 1
Ipv6.Nbr.IncompleteTimeout = 5
Ipv6.Nbr.MaxEntries = 2048
Ipv6.Nbr.ReachableTimeout = 30
Ipv6.Nbr.ReapTimeout = 3600
Ipv6.Nbr.Retries = 2
附录A:
‘-4’‘--inet4-only’‘-6’‘--inet6-only’Force connecting to IPv4 or IPv6 addresses. With ‘--inet4-only’ or ‘-4’, Wget will only connect to IPv4 hosts, ignoring AAAA records in DNS, and refusing to connect to IPv6 addresses specified in URLs. Conversely, with ‘--inet6-only’ or ‘-6’, Wget will only connect to IPv6 hosts and ignore A records and IPv4 addresses.
参考资源:
1)2012,curl_exec和wget执行太慢,IPv6惹的祸