11

これは関連する質問ですが、mechanize / urllib2に答えを適用する方法を理解できませんでした:PythonhttplibライブラリにAリクエストのみを使用させる方法

基本的に、この単純なコードを考えると:

#!/usr/bin/python
import urllib2
print urllib2.urlopen('http://python.org/').read(100)

これにより、wiresharkは次のように言います。

  0.000000  10.102.0.79 -> 8.8.8.8      DNS Standard query A python.org
  0.000023  10.102.0.79 -> 8.8.8.8      DNS Standard query AAAA python.org
  0.005369      8.8.8.8 -> 10.102.0.79  DNS Standard query response A 82.94.164.162
  5.004494  10.102.0.79 -> 8.8.8.8      DNS Standard query A python.org
  5.010540      8.8.8.8 -> 10.102.0.79  DNS Standard query response A 82.94.164.162
  5.010599  10.102.0.79 -> 8.8.8.8      DNS Standard query AAAA python.org
  5.015832      8.8.8.8 -> 10.102.0.79  DNS Standard query response AAAA 2001:888:2000:d::a2

これは5秒の遅延です。

システムのどこでもIPv6を有効にしていない(gentooでコンパイルされているUSE=-ipv6)ので、PythonにIPv6ルックアップを試す理由はないと思います。

上記の質問は、AF_INET素晴らしいサウンドのソケットタイプを明示的に設定することを提案しました。urllibを強制する方法や、自分で作成したソケットを使用するように機械化する方法がわかりません。

編集:他のアプリにも遅延があったため、AAAAクエリが問題であることがわかっています。また、ipv6を無効にして再コンパイルするとすぐに、問題は解決しました...引き続きAAAAリクエストを実行するPythonを除きます。

4

4 に答える 4

16

同じ問題に苦しんでいる、JJ によって提供された情報に基づく醜いハック (自己責任で使用してください..) を次に示します。

これは基本的に、からの呼び出しだけでなく、へのすべての呼び出しに対してそれを行う必要がある (ゼロ、で使用されているように見える) を使用する代わりにtoのfamilyパラメーターを強制します。socket.getaddrinfo(..)socket.AF_INETsocket.AF_UNSPECsocket.create_connectionurllib2socket.getaddrinfo(..)

#--------------------
# do this once at program startup
#--------------------
import socket
origGetAddrInfo = socket.getaddrinfo

def getAddrInfoWrapper(host, port, family=0, socktype=0, proto=0, flags=0):
    return origGetAddrInfo(host, port, socket.AF_INET, socktype, proto, flags)

# replace the original socket.getaddrinfo by our version
socket.getaddrinfo = getAddrInfoWrapper

#--------------------
import urllib2

print urllib2.urlopen("http://python.org/").read(100)

これは、少なくともこの単純なケースでは機能します。

于 2011-06-11T23:03:20.573 に答える
4

答えはありませんが、いくつかのデータポイントがあります。httplib.pyDNS解決はHTTPConnection.connect()(私のpython 2.5.4 stdlibの670行目)から始まっているようです

コード フローは大まかに次のとおりです。

for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    self.sock = socket.socket(af, socktype, proto)
    try:
        self.sock.connect(sa)
    except socket.error, msg: 
        continue
    break

何が起こっているかについてのいくつかのコメント:

  • 3 番目の引数はsocket.getaddrinfo()、ソケット ファミリを制限します。つまり、IPv4 と IPv6 です。ゼロを渡すと、すべてのファミリが返されます。ゼロは stdlib にハードコードされています。

  • にホスト名を渡すと、getaddrinfo()名前解決が発生します。IPv6 が有効になっている OS X ボックスでは、A レコードと AAAA レコードの両方が送信され、両方の回答がすぐに返され、両方が返されます。

  • 接続ループの残りの部分では、返された各アドレスが成功するまで試行されます。

例えば:

>>> socket.getaddrinfo("python.org", 80, 0, socket.SOCK_STREAM)
[
 (30, 1, 6, '', ('2001:888:2000:d::a2', 80, 0, 0)), 
 ( 2, 1, 6, '', ('82.94.164.162', 80))
]
>>> help(socket.getaddrinfo)
getaddrinfo(...)
    getaddrinfo(host, port [, family, socktype, proto, flags])
        -> list of (family, socktype, proto, canonname, sockaddr)

いくつかの推測:

  • のソケット ファミリはgetaddrinfo()0 にハードコードされているため、urllib でサポートされている API インターフェイスを介して A 対 AAAA レコードをオーバーライドすることはできません。mechanize が何らかの理由で独自の名前解決を行わない限り、mechanize もできません。接続ループの構造からすると、これは仕様によるものです。

  • Python のソケット モジュールは、POSIX ソケット API のシン ラッパーです。システムで利用可能および構成されているすべてのファミリを解決している思います。Gentoo の IPv6 設定を再確認してください。

于 2010-01-10T01:19:32.280 に答える
2

これの原因として最も可能性が高いのは、出力ファイアウォールの破損です。たとえば、ジュニパーファイアウォールは回避策を利用できますが、これを引き起こす可能性があります。

ネットワーク管理者にファイアウォールを修正してもらうことができない場合は、ホストベースの回避策を試すことができます。/etc/resolv.confこの行をあなたの:に追加します

options single-request-reopen

マニュアルページはそれをよく説明しています:

リゾルバーは、A要求とAAAA要求に同じソケットを使用します。一部のハードウェアは、誤って1つの応答しか返送しません。それが発生すると、クライアントシステムは座って、2番目の応答を待ちます。このオプションをオンにすると、この動作が変更され、同じポートからの2つの要求が正しく処理されない場合、2番目の要求を送信する前に、ソケットが閉じられ、新しいソケットが開かれます。

于 2012-12-03T06:50:46.990 に答える
2

DNS サーバー 8.8.8.8 (Google DNS) は、python.org の AAAA について尋ねられるとすぐに応答します。したがって、投稿したトレースでこの応答が見られないという事実は、おそらくこのパケットが戻ってこなかったことを示しています (これは UDP で発生します)。この損失がランダムである場合、それは正常です。体系的なものである場合は、ネットワークの設定に問題があることを意味します。ファイアウォールが壊れている可能性があり、最初の AAAA 応答が戻ってこない可能性があります。

5 秒の遅延は、スタブ リゾルバーから発生します。その場合、ランダムであれば運が悪いのかもしれませんが、IPv6 とは関係なく、A レコードの応答も失敗した可能性があります。

IPv6 を無効にすることは非常に奇妙に思えますが、最後の IPv4 アドレスが配布されるわずか 2 年前です!

% dig @8.8.8.8  AAAA python.org

; <<>> DiG 9.5.1-P3 <<>> @8.8.8.8 AAAA python.org
; (1 server found)
;; global options:  printcmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50323
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;python.org.                    IN      AAAA

;; ANSWER SECTION:
python.org.             69917   IN      AAAA    2001:888:2000:d::a2

;; Query time: 36 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Sat Jan  9 21:51:14 2010
;; MSG SIZE  rcvd: 67
于 2010-01-09T20:56:08.870 に答える