mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-01 17:00:21 +08:00
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:
@@ -456,3 +456,49 @@ async def test_error_stop_pattern_without_status_codes_matches_any() -> None:
|
||||
# No status_codes filter, pattern matches -> stop
|
||||
assert result.success is False
|
||||
assert attempt.await_count == 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_failover_engine_applies_backoff_every_tenth_failure(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
db = MagicMock()
|
||||
engine = FailoverEngine(db, error_classifier=_StubErrorClassifier(action=ErrorAction.BREAK))
|
||||
|
||||
sleep_mock = AsyncMock()
|
||||
rotate_mock = AsyncMock(return_value=False)
|
||||
monkeypatch.setattr("src.services.candidate.failover.asyncio.sleep", sleep_mock)
|
||||
monkeypatch.setattr(engine, "_rotate_upstream_client", rotate_mock)
|
||||
|
||||
await engine._apply_retry_pacing(
|
||||
candidate=_make_candidate(),
|
||||
consecutive_failures=10,
|
||||
error=RuntimeError("boom"),
|
||||
request_id="req-1",
|
||||
)
|
||||
|
||||
sleep_mock.assert_awaited_once()
|
||||
rotate_mock.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_failover_engine_rotates_client_on_stream_capacity_error(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
db = MagicMock()
|
||||
engine = FailoverEngine(db, error_classifier=_StubErrorClassifier(action=ErrorAction.BREAK))
|
||||
|
||||
sleep_mock = AsyncMock()
|
||||
rotate_mock = AsyncMock(return_value=True)
|
||||
monkeypatch.setattr("src.services.candidate.failover.asyncio.sleep", sleep_mock)
|
||||
monkeypatch.setattr(engine, "_rotate_upstream_client", rotate_mock)
|
||||
|
||||
await engine._apply_retry_pacing(
|
||||
candidate=_make_candidate(),
|
||||
consecutive_failures=3,
|
||||
error=RuntimeError("LocalProtocolError: Max outbound streams is 100, 100 open"),
|
||||
request_id="req-2",
|
||||
)
|
||||
|
||||
rotate_mock.assert_awaited_once()
|
||||
sleep_mock.assert_awaited_once()
|
||||
|
||||
Reference in New Issue
Block a user