mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
feat(pool,provider_ops): 扩展批量操作支持代理设置,优化签到缓存与余额刷新
- pool batch-action 新增 clear_proxy/set_proxy 操作,前端统一使用 batch-action API 替代逐个调用,批量上限从 100 提升至 2000 - batch-action 删除操作后执行 key 删除副作用(run_delete_key_side_effects) - BalanceAction 签到增加 6 小时缓存冷却,同一 host 避免重复签到 - 异步余额刷新增加 per-provider 防重入保护
This commit is contained in:
@@ -8,6 +8,8 @@ from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from src.core.cache_service import CacheService
|
||||
from src.core.logger import logger as _logger
|
||||
from src.services.provider_ops.actions.base import ProviderAction
|
||||
from src.services.provider_ops.types import (
|
||||
ActionResult,
|
||||
@@ -16,6 +18,9 @@ from src.services.provider_ops.types import (
|
||||
ProviderActionType,
|
||||
)
|
||||
|
||||
# 签到缓存 TTL(6 小时)
|
||||
_CHECKIN_CACHE_TTL = 6 * 3600
|
||||
|
||||
|
||||
class BalanceAction(ProviderAction):
|
||||
"""
|
||||
@@ -40,7 +45,7 @@ class BalanceAction(ProviderAction):
|
||||
"""
|
||||
执行余额查询(模板方法)
|
||||
|
||||
1. 先尝试签到(如果子类实现了 _do_checkin)
|
||||
1. 先尝试签到(如果子类实现了 _do_checkin),带缓存冷却
|
||||
2. 执行余额查询
|
||||
|
||||
Args:
|
||||
@@ -49,10 +54,8 @@ class BalanceAction(ProviderAction):
|
||||
Returns:
|
||||
ActionResult,其中 data 字段为 BalanceInfo
|
||||
"""
|
||||
from src.core.logger import logger
|
||||
|
||||
# 先尝试签到
|
||||
checkin_result = await self._do_checkin(client)
|
||||
# 先尝试签到(带缓存冷却)
|
||||
checkin_result = await self._get_or_do_checkin(client)
|
||||
|
||||
# 执行余额查询
|
||||
result = await self._do_query_balance(client)
|
||||
@@ -66,11 +69,11 @@ class BalanceAction(ProviderAction):
|
||||
result.data.extra["cookie_expired"] = True
|
||||
result.data.extra["cookie_expired_message"] = checkin_result.get("message", "")
|
||||
result.status = ActionStatus.AUTH_EXPIRED
|
||||
logger.warning(f"Cookie 已失效: {checkin_result}")
|
||||
_logger.warning("Cookie 已失效: {}", checkin_result)
|
||||
else:
|
||||
result.data.extra["checkin_success"] = checkin_result.get("success")
|
||||
result.data.extra["checkin_message"] = checkin_result.get("message", "")
|
||||
logger.debug(f"签到结果已附加到 extra: {checkin_result}")
|
||||
_logger.debug("签到结果已附加到 extra: {}", checkin_result)
|
||||
|
||||
return result
|
||||
|
||||
@@ -191,6 +194,39 @@ class BalanceAction(ProviderAction):
|
||||
raw_response=raw_data,
|
||||
)
|
||||
|
||||
async def _get_or_do_checkin(self, client: httpx.AsyncClient) -> dict[str, Any] | None:
|
||||
"""
|
||||
带缓存冷却的签到:优先返回缓存结果,未命中才实际执行签到。
|
||||
|
||||
缓存 key 使用 host(同一站点多个 provider 只需签到一次),TTL 6 小时。
|
||||
签到失败或 cookie_expired 不写入缓存,允许下次重试。
|
||||
"""
|
||||
host = client.base_url.host or client.base_url.netloc or str(client.base_url)
|
||||
cache_key = f"provider_ops:checkin:{host}"
|
||||
|
||||
# 检查缓存
|
||||
try:
|
||||
cached = await CacheService.get(cache_key)
|
||||
if cached is not None:
|
||||
_logger.debug("[{}] 签到缓存命中,跳过签到: {}", host, cached)
|
||||
return cached
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 缓存未命中,执行签到
|
||||
result = await self._do_checkin(client)
|
||||
|
||||
# 签到成功或"已签到"时写入缓存;失败/cookie_expired 不缓存
|
||||
if result is not None and not result.get("cookie_expired"):
|
||||
success = result.get("success")
|
||||
if success is True or success is None:
|
||||
try:
|
||||
await CacheService.set(cache_key, result, _CHECKIN_CACHE_TTL)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
async def _do_checkin(self, client: httpx.AsyncClient) -> dict[str, Any] | None:
|
||||
"""
|
||||
执行签到(子类可选实现)
|
||||
|
||||
@@ -44,6 +44,9 @@ AUTH_FAILED_CACHE_TTL = 60
|
||||
# 使用较小的值(3)确保不会对连接池造成过大压力
|
||||
_balance_refresh_semaphore: asyncio.Semaphore | None = None
|
||||
|
||||
# 正在异步刷新余额的 provider 集合(per-provider 防重入)
|
||||
_refreshing_providers: set[str] = set()
|
||||
|
||||
|
||||
def _get_balance_refresh_semaphore() -> asyncio.Semaphore:
|
||||
"""获取余额刷新信号量(延迟初始化)"""
|
||||
@@ -511,7 +514,14 @@ class ProviderOpsService:
|
||||
避免长时间占用连接池资源。
|
||||
|
||||
使用信号量限制并发数,避免启动时多个刷新任务同时运行导致连接池耗尽。
|
||||
使用 _refreshing_providers 集合防止同一 provider 被并发刷新。
|
||||
"""
|
||||
# per-provider 防重入
|
||||
if provider_id in _refreshing_providers:
|
||||
logger.debug("异步刷新余额跳过(已在刷新中): provider_id={}", provider_id)
|
||||
return
|
||||
_refreshing_providers.add(provider_id)
|
||||
|
||||
semaphore = _get_balance_refresh_semaphore()
|
||||
|
||||
# 尝试获取信号量,如果无法立即获取则跳过本次刷新
|
||||
@@ -521,6 +531,7 @@ class ProviderOpsService:
|
||||
await asyncio.wait_for(semaphore.acquire(), timeout=5.0)
|
||||
except asyncio.TimeoutError:
|
||||
logger.debug("异步刷新余额跳过(并发限制): provider_id={}", provider_id)
|
||||
_refreshing_providers.discard(provider_id)
|
||||
return
|
||||
|
||||
db = None
|
||||
@@ -538,8 +549,9 @@ class ProviderOpsService:
|
||||
db.close()
|
||||
except Exception:
|
||||
pass
|
||||
# 释放信号量
|
||||
# 释放信号量并移除防重入标记
|
||||
semaphore.release()
|
||||
_refreshing_providers.discard(provider_id)
|
||||
|
||||
async def _clear_balance_cache(self, provider_id: str) -> None:
|
||||
"""清除余额缓存"""
|
||||
|
||||
Reference in New Issue
Block a user