我正在开发和测试(通过requests)远程服务器的代码,从某天开始,我突然开始收到延迟 70-80 秒的最简单请求的响应。同时,我的电脑通过curl,wget或者浏览器发出的相同请求会立即执行,就像从第三方服务器启动的相同 Python 脚本一样。
我尝试在本地使用 Python 3.7 和 2.7 通过 httpS 和 HTTP 进行连接(显然这与 SSL 无关),结果在任何地方都是一样的。我有MacOSX 10.15.1,Ubuntu 18.04.5,NginX和Aiohttp在服务器上,但是我在Flask上提出了hello-world,结果是一样的,显然,服务器代码也没有影响。
问题:为什么请求需要这么长时间,从我的计算机到这个特定的服务器,并且只通过 Python ?!以及如何更好地调试以找到问题的原因?
我试过这样,但它没有多大帮助:
import requests
import http.client
import logging
logging.basicConfig(level=logging.DEBUG)
http.client.HTTPConnection.debuglevel = 1
logging.basicConfig()
logging.getLogger().setLevel(logging.DEBUG)
requests_log = logging.getLogger("requests.packages.urllib3")
requests_log.setLevel(logging.DEBUG)
requests_log.propagate = True
# Домен конечно изменён
response = requests.get('http://sub.test.com:5990/')
print('** Code:', response.status_code)
print('** Response:', response.content.decode('utf-8'))
结论:
DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): sub.test.com:5990
# Спустя 75 секунд:
send: b'GET / HTTP/1.1\r\nHost: sub.test.com:5990\r\nUser-Agent: python-requests/2.22.0\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nConnection: keep-alive\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: text/html; charset=utf-8
header: Content-Length: 13
header: Date: Thu, 03 Sep 2020 19:26:25 GMT
header: Server: Python/3.6 aiohttp/3.6.2
DEBUG:urllib3.connectionpool:http://sub.test.com:5990" GET / HTTP/1.1" 200 13
** Code: 200
** Response: Hello, World!
如果您在等待时终止脚本,则回溯将如下所示:
Traceback (most recent call last):
File "test.py", line 19, in <module>
response = requests.get(URL)
File "/Library/Python/3.7/site-packages/requests/api.py", line 75, in get
return request('get', url, params=params, **kwargs)
File "/Library/Python/3.7/site-packages/requests/api.py", line 60, in request
return session.request(method=method, url=url, **kwargs)
File "/Library/Python/3.7/site-packages/requests/sessions.py", line 533, in request
resp = self.send(prep, **send_kwargs)
File "/Library/Python/3.7/site-packages/requests/sessions.py", line 646, in send
r = adapter.send(request, **kwargs)
File "/Library/Python/3.7/site-packages/requests/adapters.py", line 449, in send
timeout=timeout
File "/Library/Python/3.7/site-packages/urllib3/connectionpool.py", line 672, in urlopen
chunked=chunked,
File "/Library/Python/3.7/site-packages/urllib3/connectionpool.py", line 387, in _make_request
conn.request(method, url, **httplib_request_kw)
File "/Applications/.../Python3.framework/Versions/3.7/lib/python3.7/http/client.py", line 1229, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/Applications/.../Python3.framework/Versions/3.7/lib/python3.7/http/client.py", line 1275, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "/Applications/.../Python3.framework/Versions/3.7/lib/python3.7/http/client.py", line 1224, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "/Applications/.../Python3.framework/Versions/3.7/lib/python3.7/http/client.py", line 1016, in _send_output
self.send(msg)
File "/Applications/.../Python3.framework/Versions/3.7/lib/python3.7/http/client.py", line 956, in send
self.connect()
File "/Library/Python/3.7/site-packages/urllib3/connection.py", line 184, in connect
conn = self._new_conn()
File "/Library/Python/3.7/site-packages/urllib3/connection.py", line 157, in _new_conn
(self._dns_host, self.port), self.timeout, **extra_kw
File "/Library/Python/3.7/site-packages/urllib3/util/connection.py", line 74, in create_connection
sock.connect(sa)
更新
注意到通过指定IP地址连接成功,显然问题出在DNS解析中。下落代码的类似物urllib3/util/connection.py可以毫无问题地执行:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('sub.test.com', 5990))
sock.close()
但是,发现它是在现实生活中connection.py使用的socket.AF_INET6,并且有了这个说法,连接就挂了。
解决方案
事实证明,除了正确的 IPv4 条目之外,该域在 DNSAAAA中还有一个带有无效 IPv6 地址的条目,删除该条目可以解决问题。A
事实证明,除了具有正确 IPv4 的条目之外,该域
AAAA在 DNS 中还有一个带有无效 IPv6 地址的条目。A并且 Python 显然首先尝试通过 IPv6 连接,超时时间为 75 秒,这与其他程序(curl浏览wget器)不同,它们要么有很短的超时时间,要么立即敲入 IPv4。我有一个关于 DNS 问题的假设,但大多数分析它的程序(如 viewdns.info 和 dnsdumpster.com)都没有显示
AAAA记录,所以我很早就拒绝了这个选项。在我请求修复域的 DNS 记录后,脚本开始以足够的速度运行。