mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
fix(proxy): wire AppProxyConfig.max_retries into request forwarder
The UI has exposed "请求失败时的重试次数 (0-10, default 3)" since the auto-failover panel was added, but the value was silently dropped — RequestForwarder never received it and the per-provider loop walked the whole list regardless. From the user's perspective the setting was inert. Thread AppProxyConfig.max_retries through create_forwarder into RequestForwarder, derive max_attempts = max_retries + 1 (so max_retries=0 matches the UI copy "0 retries" = single attempt), and break the loop once attempts hit the cap. The check is placed before the circuit breaker allow-permit so an over-cap iteration does not waste a HalfOpen probe slot. When auto-failover is disabled we also force max_retries to 0, mirroring how timeouts already bypass in that mode — "no failover" should mean "one provider, one try", not "limited retries against the same list".
This commit is contained in:
@@ -68,6 +68,12 @@ pub struct RequestForwarder {
|
||||
non_streaming_timeout: std::time::Duration,
|
||||
/// 流式请求响应头等待超时(秒)
|
||||
streaming_first_byte_timeout: std::time::Duration,
|
||||
/// 单个客户端请求最多尝试的 provider 数。
|
||||
///
|
||||
/// 由 `AppProxyConfig.max_retries` (UI: "请求失败时的重试次数, 0-10") 派生:
|
||||
/// `max_attempts = max_retries + 1`,所以 max_retries=0 表示仅尝试一家、
|
||||
/// max_retries=3(默认)表示最多 4 家。loop 同时受 providers.len() 自然限制。
|
||||
max_attempts: usize,
|
||||
}
|
||||
|
||||
impl RequestForwarder {
|
||||
@@ -88,7 +94,11 @@ impl RequestForwarder {
|
||||
rectifier_config: RectifierConfig,
|
||||
optimizer_config: OptimizerConfig,
|
||||
copilot_optimizer_config: CopilotOptimizerConfig,
|
||||
max_retries: u32,
|
||||
) -> Self {
|
||||
// max_retries 是「失败后重试次数」语义,attempt 上限 = retries + 1。
|
||||
// saturating_add 防止 u32::MAX + 1 溢出。
|
||||
let max_attempts = (max_retries as usize).saturating_add(1);
|
||||
Self {
|
||||
router,
|
||||
status,
|
||||
@@ -106,6 +116,7 @@ impl RequestForwarder {
|
||||
streaming_first_byte_timeout: std::time::Duration::from_secs(
|
||||
streaming_first_byte_timeout,
|
||||
),
|
||||
max_attempts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +195,17 @@ impl RequestForwarder {
|
||||
|
||||
// 依次尝试每个供应商
|
||||
for provider in providers.iter() {
|
||||
// 上限检查:尊重用户在 AppProxyConfig.max_retries 上配置的「重试次数」。
|
||||
// 放在熔断器 allow 检查之前,避免在已经超限时还占用 HalfOpen 探测名额。
|
||||
if attempted_providers >= self.max_attempts {
|
||||
log::warn!(
|
||||
"[{app_type_str}] 已达最大尝试次数上限 ({}/{}), 停止故障转移",
|
||||
attempted_providers,
|
||||
self.max_attempts
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// 发起请求前先获取熔断器放行许可(HalfOpen 会占用探测名额)
|
||||
// 单 Provider 场景下跳过此检查,避免熔断器阻塞所有请求
|
||||
let (allowed, used_half_open_permit) = if bypass_circuit_breaker {
|
||||
|
||||
@@ -211,6 +211,13 @@ impl RequestContext {
|
||||
(0, 0, 0)
|
||||
};
|
||||
|
||||
// 故障转移关闭时强制 max_retries=0(仅尝试 1 个 provider),与「不超时 + 不切换」语义一致。
|
||||
let max_retries = if self.app_config.auto_failover_enabled {
|
||||
self.app_config.max_retries
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
RequestForwarder::new(
|
||||
state.provider_router.clone(),
|
||||
non_streaming_timeout,
|
||||
@@ -227,6 +234,7 @@ impl RequestContext {
|
||||
self.rectifier_config.clone(),
|
||||
self.optimizer_config.clone(),
|
||||
self.copilot_optimizer_config.clone(),
|
||||
max_retries,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user