2017-10-19 104 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)將掩蓋但不能完全消除該問題。

相關問題