Files
awoooi/apps/api/src/core/http_client.py
OG T 196d269b92 feat: add all application source code
- apps/api: FastAPI backend with Dockerfile
- apps/web: Next.js frontend with Dockerfile
- apps/sensor: Signal collection agent
- packages: shared packages

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-22 18:57:44 +08:00

136 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
HTTP Client Manager - 永久連線池管理
=====================================
統帥鐵律: 禁止 subprocess+curl必須用 httpx AsyncClient
Features:
- Lifespan 管理 (startup/shutdown)
- 連線池復用 (Connection Pooling)
- 強制 trust_env=False (禁止 HTTP_PROXY 干擾)
- ClickHouse/SignOz 專用 Client
"""
import httpx
import structlog
from src.core.config import settings
logger = structlog.get_logger(__name__)
# =============================================================================
# Singleton Clients
# =============================================================================
_clickhouse_client: httpx.AsyncClient | None = None
_general_client: httpx.AsyncClient | None = None
# =============================================================================
# ClickHouse Client (SignOz Backend)
# =============================================================================
async def get_clickhouse_client() -> httpx.AsyncClient:
"""
取得 ClickHouse HTTP Client
配置:
- base_url: 192.168.0.188:8123 (ClickHouse HTTP API)
- trust_env: False (禁止 HTTP_PROXY 干擾)
- timeout: 30 秒
- 連線池: limits=100
"""
global _clickhouse_client
if _clickhouse_client is None or _clickhouse_client.is_closed:
_clickhouse_client = httpx.AsyncClient(
base_url=settings.CLICKHOUSE_URL.rstrip("/"),
timeout=httpx.Timeout(30.0, connect=10.0),
trust_env=False, # 🔧 關鍵: 禁止讀取 HTTP_PROXY
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
headers={
"Content-Type": "text/plain", # ClickHouse 需要 plain text
},
)
logger.info(
"clickhouse_client_initialized",
base_url=settings.CLICKHOUSE_URL,
trust_env=False,
)
return _clickhouse_client
async def init_clickhouse_client() -> httpx.AsyncClient:
"""
初始化 ClickHouse Client (在 Lifespan 啟動時調用)
"""
return await get_clickhouse_client()
async def close_clickhouse_client() -> None:
"""
關閉 ClickHouse Client (在 Lifespan 關閉時調用)
"""
global _clickhouse_client
if _clickhouse_client and not _clickhouse_client.is_closed:
await _clickhouse_client.aclose()
logger.info("clickhouse_client_closed")
_clickhouse_client = None
# =============================================================================
# General HTTP Client
# =============================================================================
async def get_general_client() -> httpx.AsyncClient:
"""
取得通用 HTTP Client (Ollama, Gemini, Claude)
"""
global _general_client
if _general_client is None or _general_client.is_closed:
_general_client = httpx.AsyncClient(
timeout=httpx.Timeout(float(settings.OPENCLAW_TIMEOUT), connect=10.0),
trust_env=False,
limits=httpx.Limits(max_connections=50, max_keepalive_connections=10),
)
logger.info(
"general_client_initialized",
timeout=settings.OPENCLAW_TIMEOUT,
)
return _general_client
async def init_general_client() -> httpx.AsyncClient:
"""初始化通用 Client"""
return await get_general_client()
async def close_general_client() -> None:
"""關閉通用 Client"""
global _general_client
if _general_client and not _general_client.is_closed:
await _general_client.aclose()
logger.info("general_client_closed")
_general_client = None
# =============================================================================
# All Clients Lifecycle
# =============================================================================
async def init_all_http_clients() -> None:
"""
初始化所有 HTTP Clients (在 Lifespan 調用)
"""
await init_clickhouse_client()
await init_general_client()
logger.info("all_http_clients_initialized")
async def close_all_http_clients() -> None:
"""
關閉所有 HTTP Clients (在 Lifespan 調用)
"""
await close_clickhouse_client()
await close_general_client()
logger.info("all_http_clients_closed")