Python`socket.getaddrinfo`花费5秒约0.1%的请求
在与各种网络服务通信的Django项目上运行Python,我们遇到一个问题,偶尔请求花费约5秒而不是通常的时间< 100 ms 。Python`socket.getaddrinfo`花费5秒约0.1%的请求
我把这个缩小到了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
w这是我们使用的,但不是来自Python本身。服务器机器运行的是Ubuntu 16.04.3 LTS,我可以用Python 2在vanilla VM上重现这一点。
我可以采取哪些措施来提高所有Python查找的性能,以便他们可以采取< 1秒?
有两件事可以做。一个是你不查询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
)将掩盖但不能完全消除该问题。
如何缓存结果并使用芹菜或类似的东西更新它们? –
听起来像你的dns解析器很慢,给ncsd一个试试吗? – georgexsh
@YaroslavSurzhikov你对缓存有什么建议?你会如何建议缓存应该更新并保持热点,以便Python服务器代码*从不*必须运行缓慢的请求,除非更新缓存? – jamesc