测速的超时钳制:2 / 8 / 30 秒这三个数是怎么用的

2026-08-10

在 cc-switch 的源码里,端点测速这件小事只占一个文件,开头三行就是全部的可调空间:

const DEFAULT_TIMEOUT_SECS: u64 = 8;
const MAX_TIMEOUT_SECS: u64 = 30;
const MIN_TIMEOUT_SECS: u64 = 2;

位置是 src-tauri/src/services/speedtest.rs 的第 8 到 10 行。很多人第一眼会把它们读成「三档超时可以选」——2 秒快测、8 秒常规、30 秒宽容。不是这么回事。这三个数扮演的是三个完全不同的角色:8 是没传值时的默认,2 和 30 是传了值也会被折回去的上下界

以下全部基于我们本地 clone 的 cc-switch 仓库快照 c39c903(提交日期 2026-08-10),仓库内版本号 3.19.2。我们只读源码文本,没有安装也没有运行过这个桌面应用

反直觉的那一处:这是 clamp,不是校验

真正决定这三个数怎么生效的,是 speedtest.rs:126-129 那几行:传进来的超时秒数如果是空的,取 DEFAULT_TIMEOUT_SECS;拿到值之后统一走一次 secs.clamp(MIN_TIMEOUT_SECS, MAX_TIMEOUT_SECS)

clamp 的语义是把越界的值折到边界上,而不是把它挡回去。落到这三个常量上就是:

上层传入实际生效原因
不传8 秒DEFAULT_TIMEOUT_SECS
0 或 12 秒MIN_TIMEOUT_SECS 抬上来
2 到 30 之间原值落在区间内,不改
60、300、999930 秒MAX_TIMEOUT_SECS 压下来

这张表的读法:左边一列是调用方给出的意图,右边一列是这次测速真正等待的上限。两列不相等的那几行,就是容易产生误解的地方——你以为自己配了个很宽松的超时,实际拿到的仍然是 30 秒;你以为自己配了个「不超时」的 0,实际拿到的是这三个数里最短的 2 秒。这个折叠过程发生在函数内部,调用方拿回来的是测速结果本身,不是「你的参数被改过」的回执。

要留意的是,这里说的是代码里的默认与边界,不是「你用起来会等多久」。请求实际花多长时间取决于网络与上游,本文不对运行结果做任何推算。

第二处反直觉:一次测速发的是两次请求

speedtest.rs:73-86 这一段还有个更容易被忽略的设计:对每一个待测 URL,代码先发一次热身请求并直接忽略结果,第二次请求才开始计时,第二次的结果才是返回给上层的那个延迟。注释给出的理由是复用连接、绕过首包惩罚。

这件事改变了「延迟」这个数字的含义。它衡量的不是冷启动——不是 DNS 解析、TCP 握手、TLS 协商全走一遍的代价,而是连接已经热过之后再打一发的往返。如果你想拿这个数去回答「我第一次连它要等多久」,那问的不是同一件事。

它同时也改变了超时这个参数的分量。钳制出来的秒数是每次请求各自的上限,而一次测速里有两次请求。所以这个数字既不是「整轮测速的总耗时上限」,也不是「你会看到的那个延迟的上限」。想把 2 / 8 / 30 用对,先把「一轮 = 热身 + 计时」这个结构记住。

这三个数管到哪儿为止

CC Switch 的后端里带「秒」的配置不止一处,混起来会很乱。把边界划清楚,这三个数的适用范围其实很窄。

它和连通性检查的 8 秒不是同一个 8。 services/stream_check.rs:50-61StreamCheckConfig::default()timeout_secs 也是 8,同一处还有 max_retries: 1degraded_threshold_ms: 6000,注释写明这个超时是从旧的 45 秒降下来的。两个 8 长得一样,来路完全不同:一个是测速服务的默认,一个是连通性检查配置结构体的默认,各自独立,改一个不会带动另一个。关于 Stream Check 本身,文档口径与代码口径有几处对不上,我们另有一篇专门讲,这里不展开。

它和代理转发的那套超时更不是一回事。 代理侧的超时是落在数据库里的:src-tauri/src/database/schema.rs:131-135proxy_config 表的列级 DEFAULT 是 streaming_first_byte_timeout 60streaming_idle_timeout 120non_streaming_timeout 600,另外还有 max_retries 3;而各应用的 seed 行又不一定取列默认值,比如 claude 那行的三个超时是 90 / 180 / 600(schema.rs:152)。这些是每个应用一行、可以改、改完落库的配置。测速这三个数则是编译进二进制的常量,不在数据库里,也不随应用区分。

两套东西连「范围」的表达方式都不同。ProxyConfig 结构体的注释(src-tauri/src/proxy/types.rs:19-27)给出的是写在注释里的建议范围:流式首字超时 1-120 秒(默认 60)、流式静默超时 60-600 秒(填 0 禁用)、非流式总超时 60-1200 秒(默认 600)。注意其中「填 0 禁用」这条——在流式转发里 0 是有意义的(response_processor.rs:700-737 里两个超时为 0 时直接不启用超时)。而在测速这边,0 不是「禁用」,是被 clamp 抬成 2。同一个 0,在两个模块里语义相反,这是最值得记一笔的地方。

它也不参与故障转移的健康判定。 代码把一条不变量写在 services/stream_check.rs:13-17(命令层在 commands/stream_check.rs:1-4 重申):连通性检查绝不触碰故障转移熔断器,熔断器只由 proxy/forwarder.rs 转发真实流量的成败被动驱动。测速服务这边我们没有读到同类的声明,但熔断这条不变量的表述本身就是排他的——它只认转发真实流量的结果。

可复现的核查动作

想自己确认上面每一句,clone 仓库后在仓库根目录做这几步:

grep -n "TIMEOUT_SECS" src-tauri/src/services/speedtest.rs
sed -n '70,90p;120,132p' src-tauri/src/services/speedtest.rs
grep -n "timeout" src-tauri/src/database/schema.rs
sed -n '15,30p' src-tauri/src/proxy/types.rs

第一条把三个常量的定义行一次列全;第二条分别对应热身请求那一段和 clamp 那一段;后两条用来比对代理侧那套完全不同的超时来源。以上为按仓库中的文件位置组合的示例命令,未经实测,行号以你 clone 到的快照为准。

判断口径也很简单:只要一个秒数是在数据库 proxy_config 表里、按应用一行的,它就跟测速无关;只要它是 speedtest.rs 顶部的 const,改它就只影响测速。

该把它调成多少

不给建议值。项目自己也没给——代码里只给了默认 8 与区间 2-30,没有任何「推荐配置」性质的文档说明这个值该怎么选。合适的数取决于你测的是什么端点、网络状况如何,这属于你自己的用法,不是本文能替你决定的。

能说清的只有「改它会牵动什么」,而且范围出乎意料地小:它只改变每次测速请求的等待上限,因而影响一个慢端点是被记成一个较大的延迟数字、还是直接被判成超时。它改变代理转发的任何超时,改变熔断阈值,改变故障转移队列的选择,改变连通性检查的行为。这几件事各有各的参数,都在别处。

什么情况说明不是这三个数的事

排查时如果出现下面这些现象,方向就不在 speedtest.rs

  • 转发中途断掉、或者长时间没有输出——那走的是流式首字超时与静默超时,配置在 proxy_config 表里,和测速的 clamp 无关。按 error_mapper.rs:7-32 的映射规则,超时与流空闲超时对外映射成 504,这也是转发链路的行为,不是测速链路的。
  • 某个供应商被跳过、请求落到了别家——那是熔断与故障转移在起作用,由转发真实流量的成败驱动,熔断参数在 proxy_config 表里另有五列。
  • 停止代理服务时卡住——server.rs:233-250 那里有一个独立的 5 秒等待保护,超时返回 ProxyError::StopTimeout,同样与测速无关。
  • 某个端点的延迟数字看起来「太好了」——回到上面第二节,那是热身之后的第二次请求,本来就不含冷启动代价。这不是异常,是设计如此。

反过来,真正指向这三个常量的情形其实只有一种:你在意的是「测一个端点最多愿意等多久」,而你给出的秒数落在 2 到 30 之外。那时你拿到的一定不是你写的那个数,而是边界值。


本文依据 CC Switch 官方仓库(github.com/farion1231/cc-switch)的 README、docs/ 下的用户手册与发布说明、src/config/ 的预设定义与 src-tauri/src/ 的后端源码整理,核对日 2026-08-10,对应仓库快照 c39c903。 本文内容为仓库源码与文档口径,我们没有安装或运行过这个桌面应用, 因此不涉及界面外观、操作手感与切换速度的任何描述。 文中出现的阈值与默认值均为源码中的默认配置,不构成对实际运行结果的保证。 该项目仍在快速迭代,版本与默认值随时可能变动,请以仓库最新内容为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。