feat(proxy,failover,transport): Hyper 上游客户端精细计时、连续失败退避与连接泄漏修复

Proxy:
- 将上游 HTTP 客户端从 reqwest 替换为 hyper,新增 InstrumentedConnector
  实现 TCP 连接/TLS 握手级别的独立计时,上报 connection_reused 等指标
- 前端展示细粒度代理计时(连接复用、等待响应头等)

Failover:
- 引入连续失败退避机制,每 10 次失败递增退避间隔
- 检测 H2 max outbound streams 错误并触发上游客户端重建
- 新增 HTTPClientPool.reset_upstream_client 支持按需重建缓存客户端

连接泄漏修复:
- Handler 异常路径确保 response_ctx 被正确关闭
- HubResponseStream 迭代结束后在 finally 块中清理 stream_id
- HubTunnelTransport.handle_request 捕获所有异常并清理流状态
This commit is contained in:
fawney19
2026-03-06 17:55:14 +08:00
parent 2269617a9f
commit 050cba9563
15 changed files with 902 additions and 72 deletions

View File

@@ -1287,6 +1287,14 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
response_ctx = None
continue
try:
if response_ctx is not None:
await response_ctx.__aexit__(None, None, None)
except Exception:
pass
finally:
response_ctx = None
from src.api.handlers.base.chat_sync_executor import ChatSyncExecutor
error_text = await ChatSyncExecutor(self)._extract_error_text(e)
@@ -1306,6 +1314,13 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
raise
except Exception:
try:
if response_ctx is not None:
await response_ctx.__aexit__(None, None, None)
except Exception:
pass
finally:
response_ctx = None
raise
# 类型断言:成功执行后这些变量不会为 None

View File

@@ -800,6 +800,14 @@ class CliStreamMixin:
response_ctx = None
continue
try:
if response_ctx is not None:
await response_ctx.__aexit__(None, None, None)
except Exception:
pass
finally:
response_ctx = None
error_text = await self._extract_error_text(e)
logger.error(
f"Provider 返回错误状态: {e.response.status_code}\n Response: {error_text}"
@@ -818,6 +826,13 @@ class CliStreamMixin:
raise
except Exception:
try:
if response_ctx is not None:
await response_ctx.__aexit__(None, None, None)
except Exception:
pass
finally:
response_ctx = None
raise
# 类型断言:成功执行后这些变量不会为 None