在与各种Web服务进行通信的Django项目上运行Python,我们遇到一个问题,偶尔请求大约需要5秒而不是通常的<100毫秒。
我把它缩小到时间了 socket.getaddrinfo
功能 - 这是被调用的 requests
当我们连接到外部服务时,它似乎也影响到集群中Postgres数据库框的默认Django连接。当我们重新开始 uwsgi
在部署之后,第一个进入的请求将花费5秒钟来发送响应。我也相信我们的芹菜任务定期需要5秒钟,但我还没有添加statsd计时器跟踪。
我写了一些代码来重现这个问题:
import socket
import timeit
def single_dns_lookup():
start = timeit.default_timer()
socket.getaddrinfo('stackoverflow.com', 443)
end = timeit.default_timer()
return int(end - start)
timings = {}
for _ in range(0, 10000):
time = single_dns_lookup()
try:
timings[time] += 1
except KeyError:
timings[time] = 1
print timings
典型的结果是 {0: 9921, 5: 79}
我的同事已经指出了围绕ipv6查找时间的潜在问题,并将其添加到了 /etc/gai.conf
:
precedence ::ffff:0:0/96 100
这肯定改进了非Python程序的查找,例如 curl
我们使用的,但不是来自Python本身。服务器盒正在运行Ubuntu 16.04.3 LTS,我可以在带有Python 2的vanilla虚拟机上重现这一点。
我可以采取哪些步骤来提高所有Python查找的性能,使它们可以<1s?
5s是DNS查找的默认超时。
你可以降低它。
你的真正问题可能是(无声)UDP数据包在网络上丢失了。
编辑: 试验 TCP的解决方案。从未这样做过。可能会帮助你。
有两件事可以做。一个是你不查询IPV6地址,这可以通过猴子修补getaddrinfo来完成
orig_getaddrinfo = socket.getaddrinfo
def _getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
return orig_getaddrinfo(host, port, socket.AF_INET, type, proto, flags)
socket.getaddrinfo = _getaddrinfo
接下来,您还可以使用基于ttl的缓存来缓存结果。您可以使用 cachepy
包装一样。
from cachetools import cached
import socket
import timeit
from cachepy import *
# or from cachepy import Cache
cache_with_ttl = Cache(ttl=600) # ttl given in seconds
orig_getaddrinfo = socket.getaddrinfo
# @cached(cache={})
@cache_with_ttl
def _getaddrinfo(host, port, family=0, type=0, proto=0, flags=0):
return orig_getaddrinfo(host, port, socket.AF_INET, type, proto, flags)
socket.getaddrinfo = _getaddrinfo
def single_dns_lookup():
start = timeit.default_timer()
socket.getaddrinfo('stackoverflow.com', 443)
end = timeit.default_timer()
return int(end - start)
timings = {}
for _ in range(0, 10000):
time = single_dns_lookup()
try:
timings[time] += 1
except KeyError:
timings[time] = 1
print (timings)
在构建缓存或monkeypatching之前,我首先尝试了解缓慢的根本原因 socket.getaddrinfo
。是否正确配置了您的名称服务器 /etc/resolv.conf
?您是否在网络上看到丢包?
如果您遇到了无法控制的损失,请运行缓存服务器(nscd
)将掩盖但不完全消除问题。