refactor(proxy): 将 proxy resolver 同步阻塞操作异步化,避免阻塞事件循环

- 为 resolve_proxy_info、resolve_delegate_config、build_proxy_url、
  get_system_proxy_config、build_post_kwargs、build_stream_kwargs 新增
  _async 异步版本,通过 asyncio.to_thread 在工作线程中执行同步 DB 查询
- 为 _proxy_node_cache 和 _system_proxy_cache 添加 threading.Lock 保护
  多线程并发读写安全
- 大 payload 的 gzip 压缩超过 64KB 阈值时走线程池,小 payload 仍在事件
  循环中同步执行以避免不必要的线程调度开销
- hub_transport 的 frame 压缩同样增加异步版本
- 删除已无调用者的同步方法 create_client_with_proxy,将其逻辑内联至
  get_upstream_client 并改为异步
- 更新所有 handler/executor/failover 调用点使用新的异步 API
- 补充 async 版本的单元测试
This commit is contained in:
fawney19
2026-03-12 13:59:21 +08:00
parent 66fec80e79
commit 0112ab752b
11 changed files with 309 additions and 162 deletions

View File

@@ -3,7 +3,15 @@ from __future__ import annotations
import gzip
import json
from src.services.proxy_node.resolver import build_post_kwargs, build_stream_kwargs
import pytest
from src.services.proxy_node.resolver import (
build_post_kwargs,
build_post_kwargs_async,
build_stream_kwargs,
build_stream_kwargs_async,
resolve_proxy_info_async,
)
class TestProxyResolverCompression:
@@ -46,3 +54,46 @@ class TestProxyResolverCompression:
)
assert all(key.lower() != "content-encoding" for key in kwargs["headers"])
@pytest.mark.asyncio
async def test_build_post_kwargs_async_compresses_when_client_sent_gzip(self) -> None:
payload = {"message": "hello", "tokens": [1, 2, 3]}
kwargs = await build_post_kwargs_async(
None,
url="https://example.com/v1/messages",
headers={"Content-Type": "application/json"},
payload=payload,
timeout=10.0,
client_content_encoding="gzip",
)
assert kwargs["headers"]["Content-Encoding"] == "gzip"
assert json.loads(gzip.decompress(kwargs["content"]).decode("utf-8")) == payload
@pytest.mark.asyncio
async def test_build_stream_kwargs_async_drops_stale_content_encoding_header(self) -> None:
kwargs = await build_stream_kwargs_async(
None,
url="https://example.com/v1/messages",
headers={"content-encoding": "gzip", "Content-Type": "application/json"},
payload={"message": "no-gzip"},
timeout=10.0,
client_content_encoding=None,
)
assert all(key.lower() != "content-encoding" for key in kwargs["headers"])
@pytest.mark.asyncio
async def test_resolve_proxy_info_async_masks_manual_proxy_url(self) -> None:
info = await resolve_proxy_info_async(
{
"enabled": True,
"url": "socks5://user:pass@proxy.example.com:1080",
}
)
assert info == {
"url": "socks5://proxy.example.com:1080",
"source": "provider",
}