fix(proxy-resolver): 将 resolve_ops_proxy_config 改为异步调用避免阻塞事件循环

在 anyrouter/nekocode/sub2api/yescode 架构及 service.py 中,
将同步的 resolve_ops_proxy_config 替换为 resolve_ops_proxy_config_async,
通过 asyncio.to_thread 包装避免同步 DB 查询阻塞事件循环。
This commit is contained in:
fawney19
2026-03-17 18:06:15 +08:00
parent 8a21cb9a55
commit 460eb5434d
7 changed files with 35 additions and 11 deletions

View File

@@ -417,9 +417,9 @@ class AnyrouterArchitecture(ProviderArchitecture):
包含 acw_cookie 的配置 包含 acw_cookie 的配置
""" """
# 从 config 获取代理配置(支持 proxy_node_id、tunnel 和旧的 proxy URL # 从 config 获取代理配置(支持 proxy_node_id、tunnel 和旧的 proxy URL
from src.services.proxy_node.resolver import resolve_ops_proxy_config from src.services.proxy_node.resolver import resolve_ops_proxy_config_async
proxy, tunnel_node_id = resolve_ops_proxy_config(config) proxy, tunnel_node_id = await resolve_ops_proxy_config_async(config)
acw_cookie = await _get_acw_cookie(base_url, proxy=proxy, tunnel_node_id=tunnel_node_id) acw_cookie = await _get_acw_cookie(base_url, proxy=proxy, tunnel_node_id=tunnel_node_id)
if acw_cookie: if acw_cookie:
return {"acw_cookie": acw_cookie} return {"acw_cookie": acw_cookie}

View File

@@ -193,9 +193,9 @@ class NekoCodeArchitecture(ProviderArchitecture):
"timeout": 10, "timeout": 10,
"verify": get_ssl_context(), "verify": get_ssl_context(),
} }
from src.services.proxy_node.resolver import resolve_ops_proxy_config from src.services.proxy_node.resolver import resolve_ops_proxy_config_async
proxy, tunnel_node_id = resolve_ops_proxy_config(config) proxy, tunnel_node_id = await resolve_ops_proxy_config_async(config)
if tunnel_node_id: if tunnel_node_id:
from src.services.proxy_node.tunnel_transport import create_tunnel_transport from src.services.proxy_node.tunnel_transport import create_tunnel_transport
@@ -215,7 +215,7 @@ class NekoCodeArchitecture(ProviderArchitecture):
return {"_usage_summary": data.get("data", {})} return {"_usage_summary": data.get("data", {})}
except Exception as e: except Exception as e:
logger.debug(f"获取 NekoCode usage summary 失败: {e}") logger.debug("获取 NekoCode usage summary 失败: {}", e)
return {} return {}

View File

@@ -479,9 +479,9 @@ class Sub2ApiArchitecture(ProviderArchitecture):
""" """
base_url = base_url.rstrip("/") base_url = base_url.rstrip("/")
from src.services.proxy_node.resolver import resolve_ops_proxy_config from src.services.proxy_node.resolver import resolve_ops_proxy_config_async
proxy, tunnel_node_id = resolve_ops_proxy_config(config) proxy, tunnel_node_id = await resolve_ops_proxy_config_async(config)
client_kwargs: dict[str, Any] = { client_kwargs: dict[str, Any] = {
"base_url": base_url, "base_url": base_url,
"timeout": 30.0, "timeout": 30.0,

View File

@@ -233,9 +233,9 @@ class YesCodeArchitecture(ProviderArchitecture):
cookie_header = _build_cookie_header(cookie_input) cookie_header = _build_cookie_header(cookie_input)
# 获取代理配置(支持 proxy_node_id、tunnel 和旧的 proxy URL # 获取代理配置(支持 proxy_node_id、tunnel 和旧的 proxy URL
from src.services.proxy_node.resolver import resolve_ops_proxy_config from src.services.proxy_node.resolver import resolve_ops_proxy_config_async
proxy, tunnel_node_id = resolve_ops_proxy_config(config) proxy, tunnel_node_id = await resolve_ops_proxy_config_async(config)
try: try:
# 构建 client 参数 # 构建 client 参数

View File

@@ -1041,9 +1041,9 @@ class ProviderOpsService:
) )
# 获取代理配置(支持 proxy_node_id、tunnel 模式和旧的 proxy URL # 获取代理配置(支持 proxy_node_id、tunnel 模式和旧的 proxy URL
from src.services.proxy_node.resolver import resolve_ops_proxy_config from src.services.proxy_node.resolver import resolve_ops_proxy_config_async
proxy, tunnel_node_id = resolve_ops_proxy_config(config) proxy, tunnel_node_id = await resolve_ops_proxy_config_async(config)
try: try:
# 构建 httpx client 参数 # 构建 httpx client 参数

View File

@@ -300,6 +300,13 @@ def resolve_ops_proxy_config(
return None, None return None, None
async def resolve_ops_proxy_config_async(
connector_config: dict[str, Any] | None,
) -> tuple[str | httpx.Proxy | None, str | None]:
"""异步解析 ops connector 代理配置,避免同步 DB 查询阻塞事件循环。"""
return await asyncio.to_thread(resolve_ops_proxy_config, connector_config)
def resolve_ops_proxy( def resolve_ops_proxy(
connector_config: dict[str, Any] | None, connector_config: dict[str, Any] | None,
) -> str | httpx.Proxy | None: ) -> str | httpx.Proxy | None:

View File

@@ -10,6 +10,7 @@ from src.services.proxy_node.resolver import (
build_post_kwargs_async, build_post_kwargs_async,
build_stream_kwargs, build_stream_kwargs,
build_stream_kwargs_async, build_stream_kwargs_async,
resolve_ops_proxy_config_async,
resolve_proxy_info_async, resolve_proxy_info_async,
) )
@@ -97,3 +98,19 @@ class TestProxyResolverCompression:
"url": "socks5://proxy.example.com:1080", "url": "socks5://proxy.example.com:1080",
"source": "provider", "source": "provider",
} }
@pytest.mark.asyncio
async def test_resolve_ops_proxy_config_async_preserves_legacy_proxy(
self,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(
"src.services.proxy_node.resolver.get_system_proxy_config",
lambda: None,
)
proxy, tunnel_node_id = await resolve_ops_proxy_config_async(
{"proxy": "http://proxy.example.com:8080"}
)
assert proxy == "http://proxy.example.com:8080"
assert tunnel_node_id is None