web-dev-qa-db-ja.com

requests.requestにmax_retriesを設定できますか?

Pythonリクエストモジュールはシンプルでエレガントですが、1つ問題があります。次のようなメッセージでrequests.exception.ConnectionErrorを取得することが可能です。

Max retries exceeded with url: ...

これは、リクエストがデータへのアクセスを数回試行できることを意味します。しかし、ドキュメント内のどこにもこの可能性についての単一の言及はありません。ソースコードを見ると、デフォルト(おそらく0)の値を変更できる場所が見つかりませんでした。

リクエストの最大再試行回数を何らかの方法で設定することは可能ですか?

139
teferi

再試行を行うのは、基になるurllib3ライブラリです。別の最大再試行回数を設定するには、 alternative transport adapters を使用します。

from requests.adapters import HTTPAdapter

s = requests.Session()
s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5))

max_retries引数は整数または Retry() object ;を取ります後者は、どの種類の障害が再試行されるかをきめ細かく制御できます(整数値は、接続障害のみを処理するRetry()インスタンスに変換されます。接続が確立された後のエラーは、副作用を引き起こす可能性があるため、デフォルトでは処理されません)。


リクエスト1.2.1のリリースより前の古い回答:

requestsライブラリーは、実際にこれを構成可能にするものでも、意図するものでもありません( this pull request を参照)。現在(要求1.1)、再試行回数は0に設定されています。本当に高い値に設定する場合は、これをグローバルに設定する必要があります。

import requests

requests.adapters.DEFAULT_RETRIES = 5

この定数は文書化されていません。将来のリリースではこの処理方法が変更される可能性があるため、ご自身の責任で使用してください。

Update:そして、このdidの変更;バージョン1.2.1では max_retriesパラメーターを設定するオプションHTTPAdapter()クラス に追加されたため、代替トランスポートアダプターを使用する必要があります。上記を参照してください。 HTTPAdapter.__init__()のデフォルトもパッチしない限り、モンキーパッチアプローチは機能しなくなります(あまりお勧めしません)。

134
Martijn Pieters

これにより、max_retriesが変更されるだけでなく、すべてのhttp://アドレスにリクエストを送信するバックオフ戦略も有効になります。 5回):

import requests
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter

s = requests.Session()

retries = Retry(total=5,
                backoff_factor=0.1,
                status_forcelist=[ 500, 502, 503, 504 ])

s.mount('http://', HTTPAdapter(max_retries=retries))

s.get('http://httpstat.us/500')

Retryのドキュメント :backoff_factorが.1の場合、sleep()は再試行の間[0.1s、0.2s、0.4s、...]の間スリープします。また、返されるステータスコードが55025、または504の場合、強制的に再試行します。

Retryのその他のさまざまなオプションにより、よりきめ細かな制御が可能になります。

  • total –許可する再試行の総数。
  • connect –再試行する接続関連のエラーの数。
  • read –読み取りエラーで再試行する回数。
  • redirect –実行するリダイレクトの数。
  • method_whitelist –再試行する必要がある大文字のHTTPメソッド動詞のセット。
  • status_forcelist –再試行を強制する必要があるHTTPステータスコードのセット。
  • backoff_factor –試行間に適用するバックオフ係数。
  • raise_on_redirect –リダイレクトの数が使い果たされた場合、MaxRetryErrorを上げるか、またはxxの範囲の応答コードで応答を返すかどうか。
  • raise_on_status – raise_on_redirectと同様の意味:ステータスがstatus_forcelistの範囲にあり、再試行が尽きた場合、例外を発生させるか、応答を返すか。

NBraise_on_statusは比較的新しく、まだurllib3またはリクエストのリリースには至っていません。 raise_on_statusキーワード引数は、最大でpythonバージョン3.6で標準ライブラリに追加されたようです。

特定のHTTPステータスコードでリクエストを再試行するには、status_forcelistを使用します。たとえば、status_forcelist = [503]はステータスコードで再試行します5(サービスは利用できません)。

デフォルトでは、次の条件でのみ再試行が起動します。

  • プールから接続を取得できませんでした。
  • TimeoutError
  • HTTPExceptionが発生しました(http.client in Python 3 else httplibから)。これは、URLやプロトコルが正しく形成されていないなど、低レベルのHTTP例外のようです。
  • SocketError
  • ProtocolError

これらはすべて、通常のHTTP応答の受信を妨げる例外です。 any通常の応答が生成された場合、再試行は行われません。 status_forcelistを使用しないと、ステータス500の応答でさえ再試行されません。

リモートAPIまたはWebサーバーを操作するためにより直感的な方法で動作させるには、上記のコードスニペットを使用します。これは、ステータスの再試行を強制します5502 =、5および504。これらはすべてウェブ上では珍しくなく、十分なバックオフ期間が与えられた場合は(おそらく)回復可能です。

編集rllibからRetryクラスを直接インポートします。

166
datashaman

Martijn Pietersの答えはバージョン1.2.1+には適していないことに注意してください。ライブラリにパッチを適用せずにグローバルに設定することはできません。

代わりにこれを行うことができます:

import requests
from requests.adapters import HTTPAdapter

s = requests.Session()
s.mount('http://www.github.com', HTTPAdapter(max_retries=5))
s.mount('https://www.github.com', HTTPAdapter(max_retries=5))
57
gizmondo

ここでいくつかの答えに少し苦労した後、 backoff というライブラリが見つかりました。基本的な例:

import backoff

@backoff.on_exception(
    backoff.expo,
    requests.exceptions.RequestException,
    max_tries=5,
    giveup=lambda e: e.response is not None and e.response.status_code < 500
)
def publish(self, data):
    r = requests.post(url, timeout=10, json=data)
    r.raise_for_status()

ライブラリのネイティブ機能を試してみることを引き続きお勧めしますが、問題が発生した場合やより広範な制御が必要な場合は、バックオフがオプションです。

11
Brad Koch

より高度な制御を取得するためのよりクリーンな方法は、リトライスタッフを関数にパッケージ化し、デコレータを使用してその関数を再試行可能にし、例外をホワイトリストに登録することです。

ここで同じものを作成しました: http://www.praddy.in/retry-decorator-whitelisted-exceptions/

そのリンクのコードを再現する:

def retry(exceptions, delay=0, times=2):
"""
A decorator for retrying a function call with a specified delay in case of a set of exceptions

Parameter List
-------------
:param exceptions:  A Tuple of all exceptions that need to be caught for retry
                                    e.g. retry(exception_list = (Timeout, Readtimeout))
:param delay: Amount of delay (seconds) needed between successive retries.
:param times: no of times the function should be retried


"""
def outer_wrapper(function):
    @functools.wraps(function)
    def inner_wrapper(*args, **kwargs):
        final_excep = None  
        for counter in xrange(times):
            if counter > 0:
                time.sleep(delay)
            final_excep = None
            try:
                value = function(*args, **kwargs)
                return value
            except (exceptions) as e:
                final_excep = e
                pass #or log it

        if final_excep is not None:
            raise final_excep
    return inner_wrapper

return outer_wrapper

@retry(exceptions=(TimeoutError, ConnectTimeoutError), delay=0, times=3)
def call_api():
4
praddy