2017-10-19 63 views
11

在与各种网络服务通信的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秒?

+0

如何缓存结果并使用芹菜或类似的东西更新它们? –

+1

听起来像你的dns解析器很慢,给ncsd一个试试吗? – georgexsh

+0

@YaroslavSurzhikov你对缓存有什么建议?你会如何建议缓存应该更新并保持热点,以便Python服务器代码*从不*必须运行缓慢的请求,除非更新缓存? – jamesc

回答

8

5s是DNS查找的默认超时。

You can lower that.

你真正的问题可能是(沉默)UDP数据包在网络上落虽然。

编辑:resolution over TCP进行实验。从来没有这样做。可以帮助你。

2

有两件事可以做。一个是你不查询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) 
2

我会先了解缓慢的根本原因,建立一个高速缓存或的Monkeypatching socket.getaddrinfo之前。您的域名服务器配置是否正确/etc/resolv.conf?你在网络上看到丢包吗?

如果遇到超出您的控制范围的损失,运行缓存服务器(nscd)将掩盖但不能完全消除该问题。

相关问题