Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/352.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python`socket.getaddrinfo`需要5秒,约占请求的0.1%_Python_Sockets_Dns_Python Requests - Fatal编程技术网

Python`socket.getaddrinfo`需要5秒,约占请求的0.1%

Python`socket.getaddrinfo`需要5秒,约占请求的0.1%,python,sockets,dns,python-requests,Python,Sockets,Dns,Python Requests,在Django项目上运行Python,该项目与各种web服务进行通信,我们遇到了一个问题,即有时请求需要大约5秒的时间,而不是通常的

在Django项目上运行Python,该项目与各种web服务进行通信,我们遇到了一个问题,即有时请求需要大约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本身。服务器盒运行的是Ubuntu16.04.3 LTS,我可以用Python2在一个普通的虚拟机上复制它


我可以采取哪些步骤来提高所有Python查找的性能,以便它们可以执行<1s?

5s是DNS查找的默认超时

但是,您真正的问题可能是(无声的)UDP数据包在网络上丢失


编辑:尝试。从来没有这样做过。可能对你有帮助。

有两件事可以做。一是不查询IPV6地址,这可以通过monkey补丁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我不确定nscd会有什么帮助-未缓存的第一个请求仍然需要5秒。在缓存值过期后,通过解析器的第一个请求将再次花费5秒。这只会减少慢速请求的百分比,而不会完全删除它们,对吗?您是否尝试使用sysctl net.IPv6.conf.all.disable_IPv6=1来禁用IPv6堆栈,并检查这是否解决了您的问题?如果是,那么您的
python
很可能链接到一个glibc版本(或静态编译),该版本不尊重gai.conf。无法在vagrant ubuntu/xenial64中本地复制它。请考虑使用
集合。Counter
而不是使用
KeyError