5

urllib2タイムアウトを考慮に入れることができないようです。私は、このトピックに関連するすべての投稿を読んだと思いますが、何も悪いことはしていないようです。私は正しいですか?ご親切にありがとうございました。

シナリオ:

残りのスクリプトに進む前に、インターネット接続を確認する必要があります。次に、以下に示す関数 (Net_Access) を作成しました。

  • LAN または Wifi インターフェースが接続された状態でこのコードを実行し、既存のホスト名を確認すると、エラーや問題がないため、タイムアウトがないため、すべて問題ありません。
  • LAN コネクタを抜いたり、存在しないホスト名をチェックしたりすると、タイムアウト値が無視されるようです。私のコードの何が問題になっていますか?

いくつかの情報:

  • Ubuntu 10.04.4 LTS (VirtualBox v4.2.6 VM で実行、ホスト OS は MAC OS X Lion)
  • cat /proc/sys/kernel/osrelease: 2.6.32-42-generic
  • パイソン 2.6.5

私のコード:

#!/usr/bin/env python

import socket
import urllib2

myhost = 'http://www.google.com'
timeout = 3

socket.setdefaulttimeout(timeout)
req = urllib2.Request(myhost)

try:
    handle = urllib2.urlopen(req, timeout = timeout)
except urllib2.URLError as e:
    socket.setdefaulttimeout(None)
    print ('[--- Net_Access() --- No network access')
else:
    print ('[--- Net_Access() --- Internet Access OK')

1) 動作中、LAN コネクタが差し込まれている

$ $ time ./Net_Access 
[--- Net_Access() --- Internet Access OK

real    0m0.223s
user    0m0.060s
sys 0m0.032s

2) タイムアウトが機能せず、LAN コネクタが取り外されている

$ time ./Net_Access 
[--- Net_Access() --- No network access

real    1m20.235s
user    0m0.048s
sys 0m0.060s

元の投稿に追加: テスト結果 (FQDN の代わりに IP を使用)

@unutbu (コメントを参照) で提案されているように、myhost の FQDN を IP アドレスに置き換えると、問題が修正されます。タイムアウトが有効になります。

LAN コネクタが接続されました...
$ time ./Net_Access [--- Net_Access() --- Internet Access OK

real    0m0.289s
user    0m0.036s
sys 0m0.040s

LAN コネクタが取り外されました...
$ time ./Net_Access [--- Net_Access() --- ネットワーク アクセスなし

real    0m3.082s
user    0m0.052s
sys 0m0.024s

これは良いことですが、タイムアウトは IP でのみ使用でき、FQDN では使用できないことを意味します。変...

DNS 解決前に取得せずに urllib2 タイムアウトを使用して IP を関数に渡す方法を誰かが見つけましたか、それとも最初にソケットを使用して接続をテストし、ターゲットに到達できることが確実なときに urllib2 を起動しますか?

どうもありがとう。

4

2 に答える 2

6

urllib2ネットワーク接続がないときにDNSルックアップがタイムアウトするまでに永遠に(または長すぎる)時間がかかることが問題である場合、はい、これは既知の問題であり、それを修正するためにそれ自体でできることは何もありません。

それで、すべての希望は失われますか?まあ、必ずしもそうとは限りません。

まず、何が起こっているのか見てみましょう。最終的には、にurlopen依存しますgetaddrinfo。これは、(のような親戚とともにgethostbyname)非同期で実行したり中断したりできないソケットAPIの重要な部分の1つです(一部のプラットフォームでは、スレッドセーフでもありません)。ソースを自分でトレースする場合は、接続の作成をurllib2延期します。これは、を呼び出し、を呼び出し、最終的には実際の関数を呼び出します。これは、世界中のすべての言語で記述されたすべてのネットワーククライアントまたはサーバーに影響を与える悪名高い問題であり、優れた簡単な解決策はありません。httplibcreate_connectionsocketsocket_getaddrinfo_socketgetaddrinfo

1つのオプションは、この問題をすでに解決している別の高レベルのライブラリを使用することです。私は、どちらが最終的に同じ問題を抱えているかにrequests依存していると思いますが、に依存しています。これは、で構築された場合、非同期で名前検索を行うため、タイムアウトになる可能性があります。urllib3pycurllibcurlc-ares

または、もちろん、twistedまたはtornadoまたは他の非同期ネットワークライブラリのようなものを使用できます。twistedしかし、明らかに、代わりにHTTPクライアントを使用するようにすべてのコードを書き直すことは、urllib2必ずしも簡単なことではありません。

urllib2別のオプションは、標準ライブラリにモンキーパッチを適用して「修正」することです。これを実行する場合は、2つのステップがあります。

まず、タイムアウト可能なを提供する必要がありますgetaddrinfo。これを行うには、バインドするか、 Linuxなどのプラットフォーム固有のAPIにアクセスするためにc-ares使用するか、ネームサーバーを検索して直接通信することもできます。しかし、それを行うための本当に簡単な方法は、スレッドを使用することです。これらをたくさん行う場合は、単一のスレッドまたは小さなスレッドプールを使用することをお勧めしますが、小規模な使用の場合は、呼び出しごとにスレッドをスピンオフするだけです。本当に迅速で汚い(読む:悪い)実装は次のとおりです。ctypesgetaddrinfo_a

def getaddrinfo_async(*args):
    result = None
    t = threading.Thread(target=lambda: result=socket.getaddrinfo(*args))
    t.start()
    t.join(timeout)
    if t.isAlive():
        raise TimeoutError(blahblahblah)
    return result

次に、これを使用するために気になるすべてのライブラリを取得する必要があります。パッチをどこにでも(そして危険に)配置したいかに応じて、socket.getaddrinfoそれ自体を置き換えることも、socket.create_connection単にコードを置き換えることhttplibもできますurllib2

最後のオプションは、これをより高いレベルで修正することです。timeoutネットワーク関連の処理がバックグラウンドスレッドで発生している場合は、全体としてより高いレベルのタイムアウトをスローできます。タイムアウトしたかどうかを判断するのに数秒以上かかった場合は、タイムアウトしていることがわかります。

于 2013-01-03T19:36:45.460 に答える
2

おそらくこれを試してください:

import urllib2

def get_header(url):
    req = urllib2.Request(url)
    req.get_method = lambda : 'HEAD'
    try:
        response = urllib2.urlopen(req)
    except urllib2.URLError:
        # urllib2.URLError: <urlopen error [Errno -2] Name or service not known>
        return False
    return True

url = 'http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.7.1.tar.bz2'
print(get_header(url))

ネットワーク アダプタを取り外すと、ほとんどすぐに False が出力されますが、通常の状態では True が出力されます。

元のコードに比べて (タイムアウト パラメータを設定する必要がなくても) なぜこれがすぐに機能するのかはわかりませんが、おそらくあなたにも機能するでしょう。


私は今朝実験をしましたが、get_headerすぐには戻らないという結果になりました。ルーターをオフにしてコンピューターを起動しました。次に、ルーターの電源を入れました。次に、Ubuntu GUI を使用してネットワークとワイヤレスを有効にしました。これにより、有効な接続を確立できませんでした。この段階で、get_headerすぐに戻ることができませんでした。

get_headerしたがって、これは、を使用してサブプロセスを呼び出す、より重いソリューションmultiprocessing.Poolです。によって返されるオブジェクトには、タイムアウト パラメータを持つメソッドがありますpool.apply_async。で指定された期間内にget結果が返されない場合、サブプロセスは終了します。get_headertimeout

したがって、check_httpすべての状況で約 1 秒以内に結果を返す必要があります。

import multiprocessing as mp
import urllib2

def timeout_function(cmd, timeout = None, args = (), kwds = {}):
    pool = mp.Pool(processes = 1)
    result = pool.apply_async(cmd, args = args, kwds = kwds)
    try:
        retval = result.get(timeout = timeout)
    except mp.TimeoutError as err:
        pool.terminate()
        pool.join()
        raise
    else:
        return retval

def get_header(url):
    req = urllib2.Request(url)
    req.get_method = lambda : 'HEAD'
    try:
        response = urllib2.urlopen(req)
    except urllib2.URLError:
        return False
    return True

def check_http(url):
    try:
        response = timeout_function(
            get_header,
            args = (url, ),
            timeout = 1)
        return response
    except mp.TimeoutError:
        return False

print(check_http('http://www.google.com'))
于 2013-01-03T16:32:05.230 に答える