'requests' python 库上的 DNS 超时

DNS timeout on 'requests' python library

对于我的项目,我必须检查网站的状态(在共享主机上)。

我使用 Python 请求库。

def getStatusCode(url):
    try:
        return requests.head(url,timeout=0.3).status_code
    except:
        return -1

此代码在 MacOS 10.10 和 Python3.4 以及 url(如 http://www.google.com)下运行良好。如果我拔掉我的 ISP 电缆,我会立即得到一个异常。

在带有 Python3.4 的 Ubuntu 服务器 14.04 下,如果我拔下 ISP 电缆,我永远不会收到超时错误。 Raspbian.

同样的问题

经过一些测试,如果我将 url 替换为 IP http://216.58.212.100,Ubuntu 服务器会向我发出异常,但由于我在共享虚拟主机上,所以我不能使用IP。

经过一些研究,我发现请求库中的超时与不是由它执行而是由 OS 执行的 DNS 查找之间存在差异。

所以我的问题是解决这个问题的最漂亮的方法是什么?我是否需要在 Python 中添加额外的超时异常,例如:Timeout on a function call

谢谢

在查理的鼓励下,我post这里有两个解决方案

对于第一个,我在请求中添加了主机 header,因此我可以将 IP 地址设为 url 并避免 DNS 查找。

def getStatusCode(url):
    headers = {'host': 'www.example.com'}
    try:
        return requests.head(url,timeout=0.3,headers=headers).status_code
    except:
        return -1

print(getStatusCode('http://1.2.3.4'))

第二种解决方案基于信号的使用,但分辨率为一秒。

class timeout:
    def __init__(self, seconds=1, error_message='Timeout'):
        self.seconds = seconds
        self.error_message = error_message
    def handle_timeout(self, signum, frame):
        raise TimeoutError(self.error_message)
    def __enter__(self):
        signal.signal(signal.SIGALRM, self.handle_timeout)
        signal.alarm(self.seconds)
    def __exit__(self, type, value, traceback):
        signal.alarm(0)

def getStatusCode(url):
    try:
        return requests.head(url,timeout=0.3).status_code
    except:
        return -1

with timeout(seconds=1):
    print(getStatusCode('http://www.example.com'))

(此解决方案来自 https://whosebug.com/a/22348885/3896729 的 Thomas Ahle)

现在我对你的问题有了更好的理解 - 我认为有更好的方法是使用你的 OS ping 应用程序,这在 Python 中应该不难做到 - for example。您还应该平均 1000 次请求并查看均值、标准差、离群值等。这样做的原因是,如果一个请求需要 500 毫秒,而您想要 1 毫秒的分辨率,您将需要产生至少 500 个请求才能获得任何东西接近你想要的分辨率。

使用 Pythons urllib(2) 的问题在于它的执行效果几乎不如系统级调用,因此您将难以生成足够多的线程来获得所需的计时分辨率.

最后,我会再次检查您的商业产品结果,以确保您的结果相似。例如(无从属关系): http://www.thinkbroadband.com/ping.