mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 09:50:21 +08:00
refactor: 拆分调度器为独立子模块,增强 Sub2API 多认证方式支持
调度器重构: - 将 CacheAwareScheduler 拆分为 candidate_builder、candidate_sorter、 concurrency_checker、restriction_checker、scheduling_config、schemas、utils 等独立模块 - 删除旧的 _candidate_builder.py 和 _candidate_sorter.py - 新增调度并发拒绝 Prometheus 指标 Sub2API 架构增强: - 支持账号密码登录和 Refresh Token 两种认证方式 - 实现 JWT 自动刷新和 Token Rotation 持久化 - 前端 ProviderAuthDialog 支持多认证方式切换和 credentials_schema 动态渲染 - 验证接口返回 updated_credentials 以同步轮换后的 token 其他改进: - 并发管理器增加 RPM guard 和动态预留逻辑 - RequestCandidate 支持 mark_skipped 附加 extra_data - TaskService 增强健壮性 - 补充相关单元测试和契约测试
This commit is contained in:
@@ -247,7 +247,13 @@ class RequestCandidateService:
|
||||
|
||||
@staticmethod
|
||||
def mark_candidate_skipped(
|
||||
db: Session, candidate_id: str, skip_reason: str | None = None
|
||||
db: Session,
|
||||
candidate_id: str,
|
||||
skip_reason: str | None = None,
|
||||
*,
|
||||
status_code: int | None = None,
|
||||
concurrent_requests: int | None = None,
|
||||
extra_data: dict | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
标记候选为已跳过
|
||||
@@ -256,12 +262,25 @@ class RequestCandidateService:
|
||||
db: 数据库会话
|
||||
candidate_id: 候选ID
|
||||
skip_reason: 跳过原因
|
||||
status_code: HTTP 状态码(可选)
|
||||
concurrent_requests: 并发请求数(这里实际记录 RPM 计数)
|
||||
extra_data: 额外数据(合并写入)
|
||||
"""
|
||||
candidate = db.query(RequestCandidate).filter(RequestCandidate.id == candidate_id).first()
|
||||
if candidate:
|
||||
candidate.status = "skipped"
|
||||
candidate.skip_reason = skip_reason
|
||||
candidate.finished_at = datetime.now(timezone.utc)
|
||||
|
||||
if status_code is not None:
|
||||
candidate.status_code = int(status_code)
|
||||
if concurrent_requests is not None:
|
||||
candidate.concurrent_requests = int(concurrent_requests)
|
||||
|
||||
if extra_data:
|
||||
base = candidate.extra_data if isinstance(candidate.extra_data, dict) else {}
|
||||
candidate.extra_data = {**base, **extra_data}
|
||||
|
||||
db.flush() # 只 flush,不立即 commit
|
||||
get_batch_committer().mark_dirty(db)
|
||||
|
||||
@@ -364,5 +383,5 @@ class RequestCandidateService:
|
||||
first_byte_epoch_ms = request_start_epoch_ms + global_first_byte_time_ms
|
||||
return max(0, int(first_byte_epoch_ms - started_at_epoch_ms))
|
||||
except Exception as e:
|
||||
logger.debug(f"计算候选 TTFB 失败: {e}")
|
||||
logger.debug("计算候选 TTFB 失败: {}", e)
|
||||
return global_first_byte_time_ms
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
@@ -34,6 +35,13 @@ class ExecutionContext:
|
||||
start_time: float | None = None
|
||||
elapsed_ms: int | None = None
|
||||
concurrent_requests: int | None = None
|
||||
rpm_current: int | None = None
|
||||
rpm_limit: int | None = None
|
||||
rpm_available_for_new: int | None = None
|
||||
reservation_ratio: float | None = None
|
||||
reservation_phase: str | None = None
|
||||
reservation_confidence: float | None = None
|
||||
reservation_load_factor: float | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -100,9 +108,13 @@ class RequestExecutor:
|
||||
key_id=key.id,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug(f"获取 RPM 计数失败(用于预留计算): {e}")
|
||||
logger.debug("获取 RPM 计数失败(用于预留计算): {}", e)
|
||||
current_key_rpm = 0
|
||||
|
||||
# 在获取 guard 之前记录当前 RPM 计数,便于并发拒绝场景落库
|
||||
context.concurrent_requests = current_key_rpm
|
||||
context.rpm_current = current_key_rpm
|
||||
|
||||
# 获取有效的 RPM 限制(自适应或固定)
|
||||
effective_key_limit = get_adaptive_rpm_manager().get_effective_limit(key)
|
||||
|
||||
@@ -113,10 +125,23 @@ class RequestExecutor:
|
||||
)
|
||||
dynamic_reservation_ratio = reservation_result.ratio
|
||||
|
||||
context.rpm_limit = effective_key_limit
|
||||
context.reservation_ratio = dynamic_reservation_ratio
|
||||
context.reservation_phase = reservation_result.phase
|
||||
context.reservation_confidence = reservation_result.confidence
|
||||
context.reservation_load_factor = reservation_result.load_factor
|
||||
|
||||
if effective_key_limit is not None and not is_cached_user:
|
||||
context.rpm_available_for_new = max(
|
||||
1, math.floor(effective_key_limit * (1 - dynamic_reservation_ratio))
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f"[Executor] 动态预留: key={key.id[:8]}..., "
|
||||
f"ratio={dynamic_reservation_ratio:.0%}, phase={reservation_result.phase}, "
|
||||
f"confidence={reservation_result.confidence:.0%}"
|
||||
"[Executor] 动态预留: key={}..., ratio={:.0%}, phase={}, confidence={:.0%}",
|
||||
key.id[:8],
|
||||
dynamic_reservation_ratio,
|
||||
reservation_result.phase,
|
||||
reservation_result.confidence,
|
||||
)
|
||||
|
||||
async with self.concurrency_manager.rpm_guard(
|
||||
@@ -131,10 +156,11 @@ class RequestExecutor:
|
||||
key_id=key.id,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug(f"获取 RPM 计数失败(guard 内): {e}")
|
||||
logger.debug("获取 RPM 计数失败(guard 内): {}", e)
|
||||
key_rpm_count = None
|
||||
|
||||
context.concurrent_requests = key_rpm_count # 用于记录,实际是 RPM 计数
|
||||
if key_rpm_count is not None:
|
||||
context.concurrent_requests = key_rpm_count # 用于记录,实际是 RPM 计数
|
||||
context.start_time = time.time()
|
||||
|
||||
response = await request_func(provider, endpoint, key, candidate)
|
||||
|
||||
Reference in New Issue
Block a user