Reuse connections. Manage lifecycle and limits.
import httpx
import asyncio
async def main():
# โ
GOOD: Share one client, reuse connections
async with httpx.AsyncClient(limits=httpx.Limits(max_connections=100)) as client:
tasks = [client.get(f"https://api.example.com/user/{i}") for i in range(1000)]
results = await asyncio.gather(*tasks)
# After the with block, all connections are closed gracefully
# โ BAD: Create new client per request
async def bad_example():
for i in range(1000):
async with httpx.AsyncClient() as client: # Opens & closes connection each time!
response = await client.get(f"https://api.example.com/user/{i}")
# New TLS handshake every iteration!
asyncio.run(main())
import asyncpg # PostgreSQL async driver
# Create pool at startup, reuse across all requests
async def init_db():
pool = await asyncpg.create_pool(
"postgresql://user:password@localhost/dbname",
min_size=10, # min connections to keep open
max_size=20, # max connections allowed
)
return pool
async def get_user(pool, user_id):
# Get a connection from pool (reuse if available, else create)
async with pool.acquire() as conn:
result = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)
return result
async def main():
pool = await init_db()
# All 1000 requests share the same 10-20 connections
tasks = [get_user(pool, i) for i in range(1000)]
results = await asyncio.gather(*tasks)
await pool.close() # Close pool on shutdown
asyncio.run(main())
| Setting | Default | Tuning |
|---|---|---|
min_size |
1-5 | Keep this many open always. Lower memory, slight latency on burst. |
max_size |
10-100 | Max to create on burst. Higher = handle spikes, but DB connection limits! |