क्या मैं अनुरोधों के लिए max_retries सेट कर सकता / सकती हूं?


182

पायथन अनुरोध मॉड्यूल सरल और सुरुचिपूर्ण है लेकिन एक बात मुझे परेशान करती है। अनुरोध प्राप्त करना संभव है। अपवाद। संदेश के साथ कनेक्ट करें जैसे:

Max retries exceeded with url: ...

इसका अर्थ है कि अनुरोध कई बार डेटा तक पहुंचने का प्रयास कर सकता है। लेकिन डॉक्स में कहीं भी इस संभावना का उल्लेख नहीं है। स्रोत कोड को देखकर मुझे कोई भी जगह नहीं मिली जहां मैं डिफ़ॉल्ट (संभवतः 0) मान को बदल सकता था।

तो क्या यह संभव है कि अनुरोधों के लिए अधिकतम संख्या को निर्धारित किया जाए?


9
2.x पर अनुरोधों के साथ इस पर कोई अद्यतन? एक अनुरोध से प्यार करेंगे। (url, max_retries = num_max_retries)) कार्यान्वयन।
परागबक्सी

11
@ अपरबक्सी: और उससे भी बेहतरrequests.get(url, max_retries=num_max_retries, dely_between_retries=3))
WoJ

1
में); @WoJ मैं अपने उदाहरण ले लिया और यह वास्तविकता बनाया just.getऔर just.postमें github.com/kootenpv/just
PascalVKooten

2
अनुरोधों के साथ रिट्रीट के
गोकुल

जवाबों:


161

यह अंतर्निहित urllib3पुस्तकालय है जो पुन: प्रयास करता है। एक अलग अधिकतम रिट्री गिनती सेट करने के लिए, वैकल्पिक परिवहन एडेप्टर का उपयोग करें :

from requests.adapters import HTTPAdapter

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

max_retriesतर्क एक पूर्णांक या एक लेता Retry()वस्तु ; उत्तरार्द्ध आपको इस बात पर ठीक-ठीक नियंत्रण देता है कि किस प्रकार की विफलताओं को वापस लिया जाता है (एक पूर्णांक मान को एक Retry()उदाहरण में बदल दिया जाता है , जो केवल कनेक्शन विफलताओं को संभालता है, एक कनेक्शन के बाद की गई त्रुटियों को डिफ़ॉल्ट रूप से नियंत्रित नहीं किया जाता है क्योंकि ये दुष्प्रभाव हो सकते हैं) ।


पुराना उत्तर, अनुरोधों को जारी करने से पहले 1.2.1 :

requestsपुस्तकालय वास्तव में इस विन्यास नहीं है, न ही यह (देखें करने का इरादा करता है इस पुल अनुरोध )। वर्तमान में (अनुरोध 1.1), रिट्रीज़ काउंट 0 पर सेट है। यदि आप वास्तव में इसे उच्च मूल्य पर सेट करना चाहते हैं, तो आपको इसे वैश्विक स्तर पर सेट करना होगा:

import requests

requests.adapters.DEFAULT_RETRIES = 5

यह स्थिरांक प्रलेखित नहीं है; अपने स्वयं के जोखिम पर इसका उपयोग करें क्योंकि भविष्य के रिलीज़ बदल सकते हैं कि यह कैसे संभाला जाता है।

अपडेट : और इस किया था परिवर्तन; संस्करण 1.2.1 में max_retriesHTTPAdapter()कक्षा पर पैरामीटर सेट करने का विकल्प जोड़ा गया था, ताकि अब आपको वैकल्पिक परिवहन एडेप्टर का उपयोग करना पड़े, ऊपर देखें। बंदर-पैच दृष्टिकोण अब काम नहीं करता है, जब तक कि आप डिफॉल्ट्स को पैच HTTPAdapter.__init__()नहीं करते हैं (बहुत अनुशंसित नहीं)।


9
यदि आपको इसकी आवश्यकता नहीं है तो आपको हर साइट के लिए इसे निर्दिष्ट करने की आवश्यकता नहीं है। आप बस कर सकते हैं session.mount('http://', HTTPAdapter(max_retries=10))यह सभी http कनेक्शन के लिए काम करेगा। तब https के साथ सभी https कनेक्शन के लिए काम करेगा।
user136036

1
@ user136036: हाँ, एडेप्टर को सबसे लंबे समय तक उपसर्ग मैच द्वारा देखा जाता है; यदि आप चाहते हैं कि यह सभी यूआरएल पर लागू हो http://और https://उपयोग करने के लिए न्यूनतम उपसर्ग हैं, तो प्रलेखन को उत्तर लिंक देखें।
मार्टिन पीटर्स

1
ध्यान दें कि HTTPAdapter(max_retries=5)केवल कुछ परिदृश्य के लिए काम करेंगे। सेNote, this applies only to failed DNS lookups, socket connections and connection timeouts, never to requests where data has made it to the server. By default, Requests does not retry failed connections.डॉक्स के अनुरोधों , किसी भी स्थिति कोड के लिए पुनः प्रयास करने के लिए, नीचे दिए गए @ datashaman के उत्तर देखें।
स्टीवन जू

@StevenXu: हाँ, आप यह Retry()बदलने के लिए कॉन्फ़िगर कर सकते हैं कि विफलता के परिदृश्य क्या हैं।
मार्टिन पीटर्स

228

यह न केवल 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 अनुसार : यदि बैकऑफ_फैक्टर 0.1 है , तो रिट्रीट के बीच [0.1s, 0.2s, 0.4s, ...] के लिए सो जाएगा ()। यदि स्थिति कोड 500 , 502 , 503 या 504 है , तो यह रिट्री को भी बाध्य करेगा ।

Retryअधिक दानेदार नियंत्रण के लिए अनुमति देने के लिए कई अन्य विकल्प :

  • कुल - रिट्रीट की कुल संख्या अनुमति देने के लिए।
  • कनेक्ट - कितने कनेक्शन से संबंधित त्रुटियों पर पुन: प्रयास करने के लिए।
  • read - कितनी बार रीड एरर पर रिट्रीट करना पड़ता है।
  • रीडायरेक्ट - कितने रीडायरेक्ट करने के लिए।
  • method_whitelist - अपरिपक्व HTTP सिस्टम का सेट क्रिया है जिसे हमें पुनः प्रयास करना चाहिए।
  • status_forcelist - HTTP स्टेटस कोड्स का एक सेट जिसे हमें एक रिट्री पर बल देना चाहिए।
  • backoff_factor - प्रयासों के बीच आवेदन करने के लिए एक बैकऑफ़ कारक।
  • raise_on_redirect - या नहीं, यदि रीडायरेक्ट की संख्या समाप्त हो रहा है, एक को बढ़ाने के लिए MaxRetryError, या में एक प्रतिक्रिया कोड वाला एक प्रतिक्रिया वापस जाने के लिए 3xx रेंज।
  • raise_on_status - करने के लिए इसी तरह के अर्थ raise_on_redirect : कि क्या हम एक अपवाद उठाना चाहिए, या एक प्रतिक्रिया वापस, स्थिति में गिर जाता है status_forcelist रेंज और पुनः समाप्त हो चुकी है।

NB : raise_on_status अपेक्षाकृत नया है, और इसे अभी तक urllib3 या अनुरोधों की रिलीज़ में नहीं बनाया है। ऐसाप्रतीत होता है कि up_on_status कीवर्ड तर्क को मानक संस्करण 3.6 में सबसे अधिक लाइब्रेरी में बनाया गया है।

विशिष्ट HTTP स्थिति कोड पर अनुरोधों को पुन: प्रयास करने के लिए, status_forcelist का उपयोग करें । उदाहरण के लिए, status_forcelist = [503] स्थिति कोड 503 (सेवा अनुपलब्ध) पर पुनः प्रयास करेगा ।

डिफ़ॉल्ट रूप से, पुनर्प्रयास केवल इन शर्तों के लिए आग लगाता है:

  • पूल से कनेक्शन नहीं मिल सका।
  • TimeoutError
  • HTTPExceptionउठाया ( http.client से पायथन 3 में अन्य नग्लिब )। ऐसा लगता है कि निम्न स्तर के HTTP अपवाद हैं, जैसे URL या प्रोटोकॉल सही तरीके से नहीं बना है।
  • SocketError
  • ProtocolError

ध्यान दें कि ये सभी अपवाद हैं जो एक नियमित HTTP प्रतिक्रिया को प्राप्त होने से रोकते हैं। यदि कोई नियमित प्रतिक्रिया उत्पन्न होती है, तो कोई पुन: प्रयास नहीं किया जाता है। Status_forcelist का उपयोग किए बिना , यहां तक ​​कि स्थिति 500 ​​के साथ एक प्रतिक्रिया भी वापस नहीं ली जाएगी।

एक दूरस्थ एपीआई या वेब सर्वर के साथ काम करने के लिए अधिक सहज ज्ञान युक्त तरीके से व्यवहार करने के लिए, मैं उपरोक्त कोड स्निपेट का उपयोग करूंगा, जो 500 , 502 , 503 और 504 स्थितियों पर पीछे हटता है। सभी असामान्य नहीं हैं। वेब और (संभवतः) पुनर्प्राप्त करने योग्य को एक बड़ा पर्याप्त बैकऑफ़ अवधि दी गई है।

संपादित करें : urllib3Retry से सीधे आयात वर्ग ।


1
मैं आपके तर्क को लागू करने की कोशिश कर रहा हूं, लेकिन मुझे नहीं पता कि क्या यह काम कर रहा है क्योंकि लॉग सिर्फ एक अनुरोध दिखाता है यहां तक ​​कि रेस स्थिति 503 भी है। मुझे कैसे पता चल सकता है कि रिट्री काम कर रहा है? कोड देखें: pastebin.com/rty4bKTw
Danilo Oliveira

1
संलग्न कोड अपेक्षित रूप से काम करता है। ट्रिक status_forcelist पैरामीटर है। यह विशिष्ट स्थिति कोड को पुनः प्राप्त करने के लिए urllib3 पैकेज को बताता है। कोड: pastebin.com/k2bFbH7Z
datashaman 16

1
urllib3 यह नहीं सोचता (और नहीं करना चाहिए) कि स्थिति 503 एक अपवाद है (डिफ़ॉल्ट रूप से)।
डेटाशमन

1
@ कोई खाता नहीं, एडेप्टर सत्र से जुड़ा हुआ है।
डेटशमैन

1
urlib3.Retry अब अनुरोधों का हिस्सा नहीं है। इसे सीधे आयात करना होगा। सुझाए गए संपादन
user2390183

59

सावधान रहें, मार्टिज़न पीटरर्स का उत्तर संस्करण 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))

22
अच्छा समाधान लेकिन ध्यान दें कि रिट्रीट के बीच कोई देरी नहीं है। यदि आप प्रयासों के बीच सोना चाहते हैं, तो आपको अपना रोल खुद करना होगा।
nofinator

18

यहाँ कुछ उत्तरों के साथ थोड़ा संघर्ष करने के बाद, मुझे एक ऐसी लाइब्रेरी मिली, जिसका बैकऑफ़ कहा गया, जिसने मेरी स्थिति के लिए बेहतर काम किया। एक मूल उदाहरण:

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()

मैं अभी भी लाइब्रेरी की मूल कार्यक्षमता को एक शॉट देने की सलाह दूंगा, लेकिन यदि आप किसी भी समस्या में भाग लेते हैं या व्यापक नियंत्रण की आवश्यकता है, तो बैकऑफ़ एक विकल्प है।


1
महान पुस्तकालय, धन्यवाद! मुझे इस कार्यशीलता की आवश्यकता कुछ और के लिए थी requests, इसलिए यह पूरी तरह से काम करता है!
डेनिस गोलोमेज़ोव

3

उच्च नियंत्रण प्राप्त करने का एक क्लीनर तरीका हो सकता है कि रिट्री स्टफ को किसी फंक्शन में पैकेज किया जाए और उस फ़ंक्शन को डेकोरेटर और वाइटेलिस्ट के अपवादों का उपयोग करके रिट्रीएबल बनाया जा सके।

मैंने यहाँ वही बनाया है: http://www.praddy.in/retry-decorator-whitelisted-exception/

उस लिंक में कोड को पुन: प्रस्तुत करना:

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():
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.