38 lines
1.1 KiB
Python
38 lines
1.1 KiB
Python
"""Shared pooled httpx.AsyncClient.
|
|
|
|
Hot paths (scrapers, Telegram send/poll, price feeds, X poster) used to build
|
|
a fresh AsyncClient per request, paying a TCP+TLS handshake every time. This
|
|
module owns one process-wide client with keep-alive pooling; callers override
|
|
the timeout per request (`client.get(url, timeout=10)`).
|
|
|
|
Lifecycle: lazily created on first use; main.py's lifespan closes it on
|
|
shutdown. Low-frequency daily jobs (KOL/macro fetchers) may keep their own
|
|
ad-hoc clients — pooling only matters on the per-second paths.
|
|
"""
|
|
import logging
|
|
from typing import Optional
|
|
|
|
import httpx
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_client: Optional[httpx.AsyncClient] = None
|
|
|
|
|
|
def get_client() -> httpx.AsyncClient:
|
|
global _client
|
|
if _client is None or _client.is_closed:
|
|
_client = httpx.AsyncClient(
|
|
timeout=httpx.Timeout(20.0),
|
|
follow_redirects=True,
|
|
limits=httpx.Limits(max_connections=50, max_keepalive_connections=20),
|
|
)
|
|
return _client
|
|
|
|
|
|
async def aclose() -> None:
|
|
global _client
|
|
if _client is not None and not _client.is_closed:
|
|
await _client.aclose()
|
|
_client = None
|