Pyyntörajoitukset ja uudelleenyrityslogiikka
Eksponentiaalinen backoff, 429-virheiden käsittely ja API:n vastuullinen käyttö
Pyyntörajoitukset ja uudelleenyrityslogiikka on ilmainen Tekoälyagentit-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Tekoälyagentit-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Tekoälyagentit-kurssilla on yhteensä 4 oppituntia.
Mitä pyyntöjen rajoittaminen tarkoittaa?
Pyyntöjen rajoittaminen on tapa, jolla API:t suojaavat itseään ylikuormitukselta. Kun agentti lähettää liian monta pyyntöä liian nopeasti, API palauttaa 429 Too Many Requests-vastauksen. Yleisiä rajoja ovat pyyntömäärät sekunnissa, minuutissa tai päivässä.
Rajoitusten sivuuttaminen johtaa estettyihin agentteihin, mitätöityihin API-avaimiin ja lisämaksuihin.
import requests
response = requests.get(
'https://api.example.com/data',
headers={'Authorization': 'Bearer YOUR_KEY'}
)
if response.status_code == 429:
print('Rate limit exceeded!')
# Check headers for limit details
limit = response.headers.get('X-RateLimit-Limit')
remaining = response.headers.get('X-RateLimit-Remaining')
reset = response.headers.get('X-RateLimit-Reset')
print(f'Limit: {limit}, Remaining: {remaining}, Reset: {reset}')Retry-After-otsake
Kun API palauttaa 429-vastauksen, se sisältää usein Retry-After-otsakkeen, joka kertoo tarkasti, kuinka monta sekuntia on odotettava ennen uudelleenyritystä. Noudattakaa aina tätä otsaketta — sen sivuuttaminen ja välitön uudelleenyritys johtaa vain uuteen 429-vastaukseen.
import requests
import time
def request_with_retry_after(url, headers):
response = requests.get(url, headers=headers)
if response.status_code == 429:
retry_after = int(response.headers.get('Retry-After', 60))
print(f'Rate limited. Waiting {retry_after} seconds...')
time.sleep(retry_after)
# Retry once after waiting
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()Eksponentiaalinen backoff
Eksponentiaalinen backoff on vakiintunut uudelleenyritysstrategia: odottakaa jokaisen epäonnistuneen yrityksen jälkeen pidempään. Jos ensimmäisellä yrityksellä odotetaan 2 sekuntia, toisella 4, kolmannella 8 ja niin edelleen. Tämä vähentää palvelimen kuormaa asteittain ja antaa sille aikaa palautua.
Kaava: wait = 2 ** attempt
import requests
import time
def get_with_exponential_backoff(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers, timeout=(5, 30))
if response.status_code == 200:
return response.json()
if response.status_code in (429, 500, 502, 503):
wait = 2 ** attempt # 1, 2, 4, 8, 16 seconds
print(f'Attempt {attempt+1} failed ({response.status_code}). '
f'Waiting {wait}s before retry...')
time.sleep(wait)
else:
response.raise_for_status() # non-retryable error
raise Exception(f'Failed after {max_retries} retries')Jitterin lisääminen backoffiin
Jos monet agentit yrittävät uudelleen samanaikaisesti (yleinen tilanne lyhyen käyttökatkon jälkeen), kaikki aktivoituvat yhtä aikaa — syntyy thundering herd -ilmiö, joka johtaa heti uuteen pyyntörajoitukseen. Jitterin (satunnaisviiveen) lisääminen hajauttaa uudelleenyrityksiä ja vähentää palvelimen kuormaa.
import requests
import time
import random
def get_with_jittered_backoff(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers, timeout=(5, 30))
if response.status_code == 200:
return response.json()
if response.status_code in (429, 500, 502, 503):
base_wait = 2 ** attempt
# Add random jitter: actual wait is 50%-100% of base
jitter = random.uniform(0.5, 1.0)
wait = base_wait * jitter
print(f'Waiting {wait:.1f}s (attempt {attempt+1})')
time.sleep(wait)
else:
response.raise_for_status()
raise Exception(f'Failed after {max_retries} retries')tenacity-kirjasto
tenacity on suosituin Python-kirjasto uudelleenyrityslogiikkaan. Se käsittelee eksponentiaalisen backoffin, jitterin, uudelleenyritysten enimmäismäärän ja mukautetut lopetusehdot selkeällä decorator-syntaksilla. Se on huomattavasti luotettavampi kuin itse kirjoitetut uudelleenyrityssilmukat.
from tenacity import (
retry, stop_after_attempt, wait_exponential,
retry_if_exception_type, before_sleep_log
)
import requests
import logging
logger = logging.getLogger(__name__)
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(multiplier=1, min=2, max=60),
retry=retry_if_exception_type(requests.exceptions.HTTPError),
before_sleep=before_sleep_log(logger, logging.WARNING)
)
def fetch_data(url, headers):
response = requests.get(url, headers=headers, timeout=(5, 30))
if response.status_code == 429:
response.raise_for_status() # triggers retry
response.raise_for_status()
return response.json()tenacity mukautetulla uudelleenyritysehdolla
Voitte määrittää tenacityn yrittämään uudelleen vain tiettyjen tilakoodien, kuten 429:n ja 5xx-virheiden, yhteydessä ja lopettamaan heti asiakasvirheisiin (4xx), joihin uudelleenyritys ei auta. Käyttäkää retry_if_result-ehtoa tai mukautettua callable-funktiota vastauksen tarkastamiseen.
from tenacity import (
retry, stop_after_attempt, wait_exponential,
retry_if_result
)
import requests
def is_retryable_response(response):
return response.status_code in (429, 500, 502, 503, 504)
@retry(
stop=stop_after_attempt(4),
wait=wait_exponential(multiplier=2, min=2, max=30),
retry=retry_if_result(is_retryable_response)
)
def resilient_get(url, headers):
response = requests.get(url, headers=headers, timeout=(5, 30))
return response # retry logic inspects the response object
# Usage
response = resilient_get(
'https://api.example.com/data',
{'Authorization': 'Bearer YOUR_KEY'}
)
data = response.json()Ennakoiva pyyntörajojen hallinta
Paras strategia on välttää pyyntörajojen saavuttaminen alun perin. Tarkistakaa jokaisen vastauksen pyyntörajoitusotsakkeet ja hidastakaa tahtia, kun lähestytte rajaa. Monet API:t sisältävät X-RateLimit-Remaining- ja X-RateLimit-Reset-otsakkeet.
import requests
import time
class RateLimitAwareClient:
def __init__(self, base_url, api_key):
self.base_url = base_url
self.headers = {'Authorization': f'Bearer {api_key}'}
self.remaining = 1000 # assume generous limit
def get(self, path):
# Proactively slow down if nearly exhausted
if self.remaining < 10:
print('Rate limit nearly exhausted, sleeping 5s...')
time.sleep(5)
response = requests.get(
f'{self.base_url}{path}', headers=self.headers
)
# Update remaining from response headers
remaining_str = response.headers.get('X-RateLimit-Remaining')
if remaining_str:
self.remaining = int(remaining_str)
response.raise_for_status()
return response.json()Uudelleenyritysten enimmäismäärä ja luovuttaminen
Uudelleenyrityslogiikalla on aina oltava raja. Loputon uudelleenyritys voi aiheuttaa ketjureaktioita, joissa kaikki agentit juuttuvat uudelleenyrityssilmukoihin. Kun max_retries-raja on saavutettu, nostakaa lopullinen poikkeus, joka sisältää tiedot epäonnistuneesta toiminnosta, jotta agentti voi lokittaa virheen ja siirtyä muihin tehtäviin.
import requests
import time
class MaxRetriesExceeded(Exception):
def __init__(self, url, attempts, last_status):
self.url = url
self.attempts = attempts
self.last_status = last_status
super().__init__(
f'Failed {url} after {attempts} attempts '
f'(last status: {last_status})'
)
def fetch_with_limit(url, headers, max_retries=3):
last_response = None
for attempt in range(max_retries):
last_response = requests.get(url, headers=headers)
if last_response.status_code == 200:
return last_response.json()
time.sleep(2 ** attempt)
raise MaxRetriesExceeded(url, max_retries, last_response.status_code)Circuit breaker -malli
Circuit breaker -malli estää agenttia kuormittamasta jatkuvasti vikaantunutta palvelua. Kun epäonnistumisten määrä ylittää tietyn rajan, piiri "avautuu" ja kaikki pyynnöt epäonnistuvat heti ilman verkkoyhteyttä. Jäähdytysajan jälkeen malli yrittää yhtä pyyntöä — jos se onnistuu, piiri sulkeutuu ja normaali toiminta jatkuu.
import time
class CircuitBreaker:
CLOSED, OPEN, HALF_OPEN = 'closed', 'open', 'half_open'
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.state = self.CLOSED
self.failures = 0
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.opened_at = None
def call(self, func, *args, **kwargs):
if self.state == self.OPEN:
if time.time() - self.opened_at > self.recovery_timeout:
self.state = self.HALF_OPEN
else:
raise Exception('Circuit OPEN — service unavailable')
try:
result = func(*args, **kwargs)
self.failures = 0
self.state = self.CLOSED
return result
except Exception as e:
self.failures += 1
if self.failures >= self.failure_threshold:
self.state = self.OPEN
self.opened_at = time.time()
print(f'Circuit OPENED after {self.failures} failures')
raise
# --- demo ---
def flaky():
raise ValueError('upstream 500')
def works():
return 'ok'
cb = CircuitBreaker(failure_threshold=3, recovery_timeout=60)
for i in range(3):
try:
cb.call(flaky)
except Exception as e:
print(f'call {i+1} failed: {e}')
print(f'Breaker state after 3 failures: {cb.state}')
try:
cb.call(flaky)
except Exception as e:
print(f'Rejected without calling flaky(): {e}')
Pyyntöjen jonottaminen rajoissa pysymiseksi
Monia kutsuja eränä tekevien agenttien kannattaa käyttää token bucketia tai yksinkertaista sleep-pohjaista rajoitinta pysyäkseen rajoissa. Laske turvallinen kutsujen välinen aika API:n pyyntörajan perusteella (esimerkiksi 60 kutsua/minuutti = yksi kutsu sekunnissa).
import requests
import time
def batch_requests(urls, headers, calls_per_minute=60):
interval = 60.0 / calls_per_minute # seconds between calls
results = []
for i, url in enumerate(urls):
start = time.time()
response = requests.get(url, headers=headers, timeout=(5, 30))
response.raise_for_status()
results.append(response.json())
print(f'Processed {i+1}/{len(urls)}')
# Sleep for remaining time in the interval
elapsed = time.time() - start
sleep_time = interval - elapsed
if sleep_time > 0:
time.sleep(sleep_time)
return resultsUudelleenyrityslogiikan yhdistäminen backoff-otsakkeisiin
Vankimmassa mallissa yhdistyvät palvelimen määrittämät odotusajat (Retry-After) ja eksponentiaalinen backoff varamenetelmänä. Suosikaa aina palvelimen ohjetta, kun se on saatavilla — palvelin tietää tarkalleen, milloin voitte yrittää uudelleen.
import requests
import time
import random
def smart_retry(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers, timeout=(5, 30))
if response.status_code == 200:
return response.json()
if response.status_code == 429:
# Use Retry-After if provided, else exponential backoff
retry_after = response.headers.get('Retry-After')
if retry_after:
wait = int(retry_after)
else:
wait = (2 ** attempt) + random.uniform(0, 1)
print(f'429 rate limit. Waiting {wait:.1f}s...')
time.sleep(wait)
elif response.status_code >= 500:
wait = (2 ** attempt) + random.uniform(0, 1)
print(f'Server error {response.status_code}. Waiting {wait:.1f}s...')
time.sleep(wait)
else:
response.raise_for_status() # non-retryable
raise Exception(f'Gave up after {max_retries} attempts')Pikatarkistus: eksponentiaalinen backoff
Testatkaa ymmärrystänne uudelleenyritysstrategioista.
Pyyntörajoituksen ja uudelleenyritysten kertaus
Agenttinne osaavat nyt käsitellä pyyntörajoituksia hallitusti:
- 429 Too Many Requests — noudattakaa
Retry-After-otsaketta ja odottakaa ennen uudelleenyritystä - Eksponentiaalinen backoff —
wait = 2^attemptkaksinkertaistaa odotusajan jokaisella uudelleenyrityksellä - Jitter — lisää satunnaisuutta ja hajauttaa uudelleenyritykset useiden agentti-instanssien kesken
- tenacity — hoitaa kaiken uudelleenyrityslogiikan decoratorien ja selkeän määrityksen avulla
- Circuit breaker — estää vikaantuneen palvelun jatkuvan kuormittamisen rajan ylityttyä
- Ennakoiva rajoittaminen — tarkistakaa
X-RateLimit-Remainingja hidastakaa tahtia ennen rajan saavuttamista
Opi Tekoälyagentit tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 60
- Oppitunnit
- 239
Usein kysytyt kysymykset
Onko oppitunti ”Pyyntörajoitukset ja uudelleenyrityslogiikka” ilmainen?
Kyllä – oppitunnin ”Pyyntörajoitukset ja uudelleenyrityslogiikka” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Tekoälyagentit-kurssin, päivitä CoddyKit PROhon. Tekoälyagentit-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Pyyntörajoitukset ja uudelleenyrityslogiikka”?
Eksponentiaalinen backoff, 429-virheiden käsittely ja API:n vastuullinen käyttö Harjoittelet Tekoälyagentit-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Tekoälyagentit-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Tekoälyagentit-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.
Kuinka kauan ”Pyyntörajoitukset ja uudelleenyrityslogiikka”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Tekoälyagentit-oppitunnilla?
Kyllä. Jokainen Tekoälyagentit-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- REST API -perusteet agenttikehittäjille
- Todennus: API-avaimet ja OAuth
- API-vastausten ja virheiden käsittely
- Pyyntörajoitukset ja uudelleenyrityslogiikka