Chapter 7 · Timeouts and Retries
Chapter goals: master timeout configuration and retry strategies in requests and httpx so requests never hang forever or retry endlessly.
7.1 Why You Need Timeouts
Requests without timeouts are one of the most common sources of production incidents. Picture this: your service calls a third-party payment API and their server goes down without closing the TCP connection — your request hangs indefinitely until every worker thread is exhausted, and your whole service cascades down.
import requests
# Dangerous: no timeout, could hang forever
resp = requests.get('https://slow-api.example.com/data')
# Correct: set a timeout
resp = requests.get('https://slow-api.example.com/data', timeout=5)Timeouts have two dimensions:
- Connect timeout: the longest wait for the TCP handshake to complete;
- Read timeout: the longest wait for the server to return data.
7.2 Timeout Configuration in requests
The timeout argument in requests accepts two forms:
import requests
# Option 1: a single value (connect + read share it)
resp = requests.get('https://httpbin.org/delay/1', timeout=5)
# Option 2: a tuple (connect_timeout, read_timeout)
resp = requests.get('https://httpbin.org/get', timeout=(3, 10))
# connect within 3 seconds; read the response within 10When a timeout fires, requests raises requests.exceptions.Timeout:
try:
resp = requests.get('https://httpbin.org/delay/10', timeout=2)
except requests.exceptions.Timeout:
print('Request timed out')7.3 Retries with HTTPAdapter
requests implements automatic retries via urllib3's Retry mechanism. First register an HTTPAdapter:
import requests
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
# Build a retry strategy
retry_strategy = Retry(
total=3, # max retries
backoff_factor=1, # intervals: 0s, 1s, 2s...
status_forcelist=[429, 500, 502, 503, 504], # only retry these statuses
allowed_methods=["GET", "POST"] # only retry these methods
)
# Mount onto a session
session = requests.Session()
session.mount('https://', HTTPAdapter(max_retries=retry_strategy))
session.mount('http://', HTTPAdapter(max_retries=retry_strategy))
# Requests through the session now retry automatically
resp = session.get('https://httpbin.org/status/500')backoff_factor controls retry waits: the Nth retry waits backoff_factor × (2^(N-1)) seconds. With backoff_factor=1, intervals are 0, 1, 2, 4 seconds.
7.4 Timeout Configuration in httpx
httpx has a finer-grained timeout system with four timeout types:
import httpx
# Option 1: single timeout
with httpx.Client(timeout=10.0) as client:
resp = client.get('https://httpbin.org/get')
# Option 2: fine-grained configuration
timeout = httpx.Timeout(
connect=3.0, # connect within 3 seconds
read=10.0, # read within 10 seconds
write=30.0, # write within 30 seconds
pool=5.0 # wait at most 5 seconds for a pooled connection
)
with httpx.Client(timeout=timeout) as client:
resp = client.get('https://httpbin.org/get')httpx raises dedicated exceptions on timeout: ConnectTimeout, ReadTimeout, WriteTimeout, PoolTimeout.
7.5 Combining Retries and Timeouts
Production environments usually need both timeouts and retries together:
import httpx
from httpx import Timeout
# Fine-grained timeouts + retries
timeout = Timeout(connect=5.0, read=30.0, write=10.0, pool=5.0)
limits = httpx.Limits(max_connections=100, max_keepalive_connections=20)
with httpx.Client(timeout=timeout, limits=limits) as client:
try:
resp = client.get('https://api.example.com/data')
resp.raise_for_status()
except httpx.ConnectTimeout:
print('Connect timeout')
except httpx.ReadTimeout:
print('Read timeout')
except httpx.HTTPStatusError as e:
print(f'HTTP error: {e.response.status_code}')7.6 Chapter Summary
- Timeouts are essential production protection against hanging requests;
- requests uses a
(connect, read)tuple for dual timeouts; - httpx provides a
Timeoutobject supporting connect/read/write/pool; - Smart retries come from
HTTPAdapter+Retry; combine withbackoff_factorto avoid avalanches.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Which formats does the requests timeout argument accept?
2. With urllib3 Retry backoff_factor=1, how long is the wait before the third retry?
3. Which of these is NOT an httpx timeout type?
4. Which approach best fits production HTTP calls?
🛠️ Hands-On Exercises
- Test timeouts: call
https://httpbin.org/delay/5with a 2-second timeout; catch and print the exception. - Build a session with retries: retry
https://httpbin.org/status/500up to 3 times with 1-second intervals and log each retry. - Configure fine-grained timeouts in httpx and compare the four timeout exception types.
After finishing the exercises, move on to the next chapter.