2017-10-19 1 views
11

다양한 웹 서비스와 통신하는 Django 프로젝트에서 Python을 실행하면 때때로 평소에 < 100ms가 아닌 약 5 초가 걸리는 문제가 있습니다 .Python`socket.getaddrinfo` 요청이 약 0.1 초 정도 걸리는 것

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 

curl과 같은 비 Python 프로그램의 조회가 확실히 향상되었습니다. 우리는 사용하지만 Python 자체에서는 사용하지 않습니다. 서버 박스는 Ubuntu 16.04.3 LTS를 실행 중이고 파이썬 2를 사용하여 바닐라 VM에서 이것을 재현 할 수 있습니다.

모든 파이썬 조회의 성능을 향상시키기 위해 취할 수있는 조치는 무엇입니까 < 1s ?

+0

결과를 캐싱하고 셀러리 등을 사용하여 결과를 갱신하는 것은 어떻습니까? –

+1

당신의 DNS 리졸버가 느린 것처럼 들리지만, ncsd를 시도해보십시오. – georgexsh

+0

@YaroslavSurzhikov 캐시에 대해 어떤 제안을 하시겠습니까?그리고 파이썬 서버 코드 *가 * 캐시를 업데이트 할 때를 제외하고는 느린 요청을 실행해야하므로 캐시를 업데이트하고 유지해야한다고 제안하는 방법은 무엇입니까? – jamesc

답변

8

5s는 DNS 조회의 기본 시간 초과입니다.

You can lower that.

당신의 진짜 문제는 아마 (자동) UDP 패킷이 비록 네트워크에 드롭입니다.

편집 :resolution over TCP으로 실험하십시오. 절대 한 적이 없어. 당신을 도울 수 있습니다.

2

두 가지 작업을 수행 할 수 있습니다. 하나는 당신이 IPv6 주소를 조회하지 않는다는 것입니다, 이것은 당신이 또한 결과를 캐시하는 TTL 기반 캐시를 사용할 수 있습니다

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 

다음 getaddrinfo를 원숭이 패치하여 수행 할 수 있습니다. 동일한 패키지로 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

내가 먼저 캐시를 구축하거나 socket.getaddrinfo을 monkeypatching 전에 속도 저하의 근본적인 원인을 이해하려고 노력한다. 네임 서버가 /etc/resolv.conf에 올바르게 구성되어 있습니까? 네트워크에서 패킷 손실이 보이십니까?

통제 범위를 벗어난 손실이 발생하는 경우 캐싱 서버 (nscd)를 실행하면 문제가 완전히 제거되지는 않지만 완전히 제거되지는 않습니다.

관련 문제