我们有一个批处理作业,每天在AWS的EC2实例上运行。 VPC中存在EC2实例。批处理作业使用Java在公共服务器上进行一系列REST API调用。大多数情况下,批处理作业运行都没有问题。但是,有些时候,DNS解析出现了问题。作业将愉快地运行,然后DNS解析突然失败,剩下的API调用会出错,但出现以下异常:
java.net.UnknownHostException: some.publicserver.com: Name or service not known
at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method) ~[na:1.8.0_191]
at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:929) ~[na:1.8.0_191]
at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1324) ~[na:1.8.0_191]
at java.net.InetAddress.getAllByName0(InetAddress.java:1277) ~[na:1.8.0_191]
at java.net.InetAddress.getAllByName(InetAddress.java:1193) ~[na:1.8.0_191]
at java.net.InetAddress.getAllByName(InetAddress.java:1127) ~[na:1.8.0_191]
at org.apache.http.impl.conn.SystemDefaultDnsResolver.resolve(SystemDefaultDnsResolver.java:44) ~[batchjob.jar:na]
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:102) ~[batchjob.jar:na]
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:319) ~[batchjob.jar:na]
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363) ~[batchjob.jar:na]
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219) ~[batchjob.jar:na]
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195) ~[batchjob.jar:na]
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86) ~[batchjob.jar:na]
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108) ~[batchjob.jar:na]
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184) ~[batchjob.jar:na]
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) ~[batchjob.jar:na]
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106) ~[batchjob.jar:na]
...
有时,每个API调用都会因此错误而失败,有时,将有一串成功的调用,然后一切都将开始失败。在工作失败的日子里,我可以同时连接服务器并验证DNS似乎可以正常工作。例如,如果我使用以下命令
nslookup some.publicserver.com
它返回成功的响应。同时,批处理作业会喷出一堆UnknownHostExceptions。
对于在哪里寻找问题根源,我感到困惑。外面有没有人经历过类似的事情?
答案 0 :(得分:0)
我认为这不是每个Java特定的问题,而是EC2实例的DNS解析问题。 Java首先将通过检查hosts
文件,然后调用底层操作系统的DNS相关功能来有效地执行DNS解析操作。
考虑到这一点以及底层EC2实例有效运行Linux发行版的事实,这些步骤将导致对OS的gethostbyname2
函数的调用。反过来,这将执行所有隐藏的魔术,以解决有问题的名称。
现在,有两件事对于解决您的问题非常重要。第一个是您正在呼叫的服务器的IP地址是否经常更改。二是您使用的nslookup
程序将直接查询DNS服务器。这意味着Java尝试解析域名的程序与程序执行的程序之间可能会存在差异。此外,这也可能意味着OS可能已经缓存了与服务器最新地址不对应的IP地址。因此,我建议使用其他实用程序(例如ping
)检查主机名的IP地址。
有关此问题的最佳建议是:
在尝试执行主机名时添加某种日志跟踪
分辨率并将其与nslookup
的解析值进行比较。
检查EC2是否具有正确的DNS设置(什么DNS服务器 您正在使用等)。
向hosts
文件添加条目,将域名映射到IP
地址(前提是后者不变)。
希望以上帮助。
答案 1 :(得分:0)
在我的特定情况下,这是值得的,这就是我能够弄清楚的。希望这会帮助其他人。
在我的示例(some.publicserver.com)中,目标的权威DNS服务器正在为某些请求返回SERVFAIL。这似乎是一个负载问题,因为它似乎在一整天中偶尔发生。通过我的AWS设置,我正在为我的VPC使用默认的DNS服务器,该服务器由AWS提供。这些服务器显然不进行任何缓存。我了解到Java通过InetAddress为DNS解析做了一些缓存,但是默认情况下它是一个短窗口(我相信大多数实现中30秒)。
因此,最终,该问题的真正原因是some.publicserver.com的权威DNS服务器并不完全可靠。由于我无法控制这些服务器,因此我认为最好的解决方法是使用DNS缓存。选项1是在我的EC2 Ubuntu实例上使用本地DNS缓存(类似于dnsmasq)。选项2是通过执行以下操作来增加Java使用的缓存持续时间:
java.security.Security.setProperty("networkaddress.cache.ttl" , "900");
我选择了选项#2,因为它所需的工作量更少,并且将潜在的副作用降至最低。因此,它为我解决了这个问题。