2017-10-19 11 views
11

さまざまなWebサービスと通信するDjangoプロジェクトでPythonを実行すると、時々リクエストが通常の< 100ミリ秒ではなく約5秒かかることがあるという問題があります。Python `socket.getaddrinfo`要求の約0.1%を受け取ります

これは、socket.getaddrinfoの機能で取られた時間に絞りました。これは、外部サービスに接続するときにrequestsによって呼び出されますが、デフォルトのDjango接続をクラスタのPostgresデータベースボックスに反映するようにも見えます。デプロイ後に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を実行しており、これをPython 2のバニラVMで再現することができます。

すべてのPythonルックアップのパフォーマンスを向上させるために、どのような手順を取ることができますか。< 1 ?

+0

結果をキャッシングしてセロリなどで更新するのはどうですか? –

+1

あなたのDNSリゾルバが遅いのが聞こえますが、ncsdに試してみてください。 – georgexsh

+0

@ YaroslavSurzhikovあなたはキャッシュのために何を提案しますか?そして、あなたは、キャッシュを更新して高温にして、Pythonサーバーコード*がキャッシュを更新するとき以外は遅い要求を実行しないようにする方法を提案しますか? – jamesc

答えて

8

5秒はDNSルックアップのデフォルトのタイムアウトです。

You can lower that.

あなたの本当の問題は、おそらく(サイレント)UDPパケットはしかし、ネットワーク上のドロップです。

編集:resolution over TCPで実験してください。決してそれをしなかった。あなたを助けるかもしれない。

2

実行できることは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)を実行すると、問題は完全に排除されますが、完全に排除されるわけではありません。

関連する問題