Khi Requests không còn đủ nhanh cho nhu cầu hiện đại
Trong giới Python, requests giống như một vị vua không ngai. Nó đơn giản, ổn định và cực kỳ phổ biến. Tuy nhiên, khi mình bắt đầu triển khai các hệ thống monitoring alert và script deploy trên production chạy asyncio, requests bỗng trở thành nút thắt cổ chai khiến toàn bộ hệ thống chậm lại.
Vấn đề nằm ở cơ chế đồng bộ (blocking). Thử tưởng tượng script của bạn cần gọi 50 API, mỗi API mất 1 giây phản hồi. Với requests, bạn mất đúng 50 giây vì chương trình phải đợi từng request một. Trong các ứng dụng cần độ trễ thấp như FastAPI hay Bot Telegram, việc để CPU “ngồi chơi” chờ mạng là một sự lãng phí khủng khiếp. Đó là lúc mình tìm đến httpx.
Mình đã chuyển dịch toàn bộ công cụ automation sang httpx được hơn nửa năm. Kết quả thật bất ngờ. Nó không chỉ giải quyết bài toán async/await mà còn mang tới những tính năng mà requests vẫn chưa có: HTTP/2 và cơ chế Connection Pooling thực thụ.
Httpx có gì mà khiến giới Dev Python phát sốt?
Nói một cách dễ hiểu, httpx là phiên bản “nâng cấp động cơ” của requests. Nó giữ nguyên bộ API quen thuộc giúp bạn chuyển đổi code chỉ trong 1 nốt nhạc nhưng bên dưới là một hệ thống xử lý hoàn toàn khác biệt.
Những ưu điểm vượt trội:
- Hỗ trợ Async toàn diện: Tương thích hoàn hảo với
asynciovàtrio. - Chuyển đổi dễ dàng: Bạn gần như chỉ cần thay
import requeststhànhimport httpx. - HTTP/2 Multiplexing: Gửi nhiều request trên một kết nối TCP duy nhất. Việc này giúp giảm latency từ 200ms xuống còn khoảng 40-50ms cho các request kế tiếp.
- Quản lý Connection Pool: Tái sử dụng kết nối thông minh, loại bỏ bước handshake SSL/TLS tốn kém thời gian.
- Strict Timeouts: Tránh tình trạng script bị treo vô hạn nhờ cấu hình timeout mặc định chặt chẽ.
Cài đặt và cấu hình
Bạn có thể cài đặt bản tiêu chuẩn hoặc bản full hỗ trợ HTTP/2 qua pip. Mình khuyên dùng bản full để tận dụng tối đa sức mạnh.
pip install httpx[http2]
Chế độ Sync: Quen thuộc như ở nhà
Nếu bạn chưa cần đến async, httpx vẫn phục vụ tốt các tác vụ truyền thống với cú pháp không khác gì đàn anh:
import httpx
# Cú pháp y hệt requests
resp = httpx.get("https://api.github.com/repos/encode/httpx")
print(f"Status: {resp.status_code} - Repo: {resp.json()['name']}")
Chế độ Async: Đánh bại mọi giới hạn tốc độ
Đây là lý do thực sự để bạn chuyển sang httpx. Khi cần crawl dữ liệu từ 100 website hoặc bắn 50 thông báo đồng thời, async sẽ giúp bạn tiết kiệm hàng chục giây quý giá.
import httpx
import asyncio
async def fetch_data():
async with httpx.AsyncClient() as client:
resp = await client.get("https://www.google.com")
print(f"Done: {resp.status_code}")
asyncio.run(fetch_data())
Lưu ý: Hãy luôn dùng AsyncClient trong khối async with. Thói quen này giúp tự động đóng kết nối, tránh rò rỉ tài nguyên (resource leak) khi hệ thống vận hành lâu dài.
Tối ưu hiệu năng với Connection Pooling
Nhiều bạn mắc lỗi khởi tạo Client mới cho mỗi request. Việc này khiến script chạy chậm vì phải thực hiện lại quá trình bắt tay (handshake) TCP/SSL cực kỳ tốn thời gian. Connection Pooling sinh ra để chấm dứt sự lãng phí này.
Hãy giữ một instance Client duy nhất cho toàn bộ vòng đời ứng dụng của bạn:
import httpx
import asyncio
import time
async def main():
# Khởi tạo một lần, dùng cho hàng ngàn request
async with httpx.AsyncClient() as client:
urls = ["https://httpbin.org/get"] * 50
start = time.perf_counter()
tasks = [client.get(url) for url in urls]
results = await asyncio.gather(*tasks)
duration = time.perf_counter() - start
print(f"Xử lý {len(results)} requests trong {duration:.2f}s")
asyncio.run(main())
Thực tế trên production, việc dùng pooling giúp mình giảm tới 40% tổng thời gian thực thi đối với các API yêu cầu HTTPS khắt khe.
Bứt tốc với HTTP/2
HTTP/1.1 khá cũ kỹ khi mỗi request thường chiếm dụng một kết nối riêng. Ngược lại, HTTP/2 cho phép đẩy nhiều request đi cùng lúc trên một “đường ống” duy nhất. Để kích hoạt, bạn chỉ cần thêm một tham số đơn giản.
async with httpx.AsyncClient(http2=True) as client:
resp = await client.get("https://www.google.com")
print(f"Version: {resp.http_version}") # Output: HTTP/2
Nếu server đích không hỗ trợ HTTP/2, httpx sẽ tự động lùi về (fallback) HTTP/1.1 một cách êm ái mà không gây lỗi chương trình.
Xử lý lỗi và Timeout trên Production
Mặc định httpx để timeout là 5 giây. Con số này đôi khi quá ngắn với các API xử lý dữ liệu nặng. Mình thường cấu hình chi tiết để script hoạt động bền bỉ hơn:
# Kết nối nhanh (2s), nhưng cho phép đọc dữ liệu lâu hơn
timeout = httpx.Timeout(10.0, connect=2.0, read=None)
try:
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.get("https://slow-api.com")
resp.raise_for_status()
except httpx.ConnectTimeout:
print("Server không phản hồi bước handshake!")
except httpx.HTTPStatusError as exc:
print(f"Lỗi {exc.response.status_code} tại {exc.request.url}")
except httpx.RequestError:
print("Lỗi mạng không xác định")
Lời kết từ kinh nghiệm thực tế
Chuyển sang httpx không đơn thuần là chạy theo công nghệ mới. Đó là sự nâng cấp về tư duy khi bạn bắt đầu xây dựng các hệ thống Python hiệu năng cao.
Với các script nhỏ chạy một lần, requests vẫn là người bạn đồng hành tốt. Nhưng nếu bạn đang làm việc với FastAPI, xây dựng crawler tốc độ cao, hoặc quản lý hàng ngàn request mỗi phút, httpx là lựa chọn không thể thay thế. Khả năng debug rõ ràng của nó sẽ cứu nguy cho bạn rất nhiều lần khi vận hành trên production.

