代理功能
超时控制
超时控制是 Claude Code Hub 代理服务的重要组成部分,用于管理 LLM API 请求的生命周期。系统实现了多层超时机制,确保请求不会无限期挂起,同时支持流式响应的空闲检测。
为什么需要超时控制
在与 LLM 供应商交互时,可能遇到以下问题:
- 响应延迟:某些供应商可能由于负载过高而响应缓慢
- 连接挂起:网络问题导致请求长时间无响应
- 流式中断:流式响应中途停止发送数据
- 资源占用:长时间运行的请求占用连接池和内存
超时控制通过设置合理的等待时间上限,确保系统能够快速检测问题并触发故障转移。
双路径超时机制
系统根据请求类型采用不同的超时策略:
| 请求类型 | 超时参数 | 作用 |
|---|---|---|
| 流式请求 | firstByteTimeoutStreamingMs | 控制等待首字节响应的时间 |
| 非流式请求 | requestTimeoutNonStreamingMs | 控制整个请求的总超时时间 |
流式请求超时
流式请求使用首字节超时机制:
- 建立连接后,等待供应商返回第一个数据块
- 如果在
firstByteTimeoutStreamingMs内未收到首字节,触发超时 - 收到首字节后,切换到空闲检测模式
这种设计允许快速识别响应缓慢的供应商,同时不限制长文本生成的总时长。
非流式请求超时
非流式请求使用总超时机制:
- 从发送请求开始计时
- 如果在
requestTimeoutNonStreamingMs内未完成响应,触发超时 - 适用于一次性返回完整响应的场景
流式空闲检测
对于流式响应,系统实现了空闲超时检测(静默期监控):
空闲检测原理
空闲检测器监控数据流的接收间隔。每当收到新的数据块,计时器重置;如果在 streamingIdleTimeoutMs 内未收到任何数据,判定为空闲超时。
超时触发动作
当流式空闲超时发生时,系统执行以下操作:
- 关闭客户端流:向客户端发送错误通知,结束流式连接
- 中止上游连接:终止与供应商的连接,防止资源泄漏
- 停止后台任务:取消正在进行的读取任务
客户端断开处理
当客户端主动断开连接时:
- 立即清除空闲计时器
- 取消后台读取任务
- 上游连接自然结束(不强制中断)
这种设计避免不必要的资源浪费,同时允许供应商完成其内部处理。
配置参数
供应商级超时配置
每个供应商可以独立配置超时参数,存储在数据库中:
| 参数 | 字段名 | 默认值 | 范围 | 说明 |
|---|---|---|---|---|
| 流式首字节超时 | firstByteTimeoutStreamingMs | 0 | 1000-180000 ms | 等待首字节的时间,0 表示无限制 |
| 流式空闲超时 | streamingIdleTimeoutMs | 0 | 60000-600000 ms | 流式响应的空闲检测时间,0 表示无限制 |
| 非流式请求超时 | requestTimeoutNonStreamingMs | 0 | 60000-1800000 ms | 非流式请求的总超时时间,0 表示无限制 |
零值含义
将超时参数设置为 0 会禁用对应的超时检测(相当于无限等待)。建议为生产环境的供应商设置合理的超时值。
全局环境变量
系统级别的底层超时配置:
| 变量名 | 默认值 | 说明 |
|---|---|---|
FETCH_CONNECT_TIMEOUT | 30000 | TCP 连接超时(毫秒),包括 DNS 解析和 TLS 握手 |
FETCH_HEADERS_TIMEOUT | 600000 | 等待响应头/首字节超时(毫秒) |
FETCH_BODY_TIMEOUT | 600000 | 请求/响应体传输超时(毫秒) |
API_TEST_TIMEOUT_MS | 15000 | API 测试请求超时(毫秒),范围 5000-120000 |
这些环境变量控制底层 HTTP 客户端(undici)的行为,覆盖其默认的 300 秒超时限制。
API 测试特殊处理
API 测试使用独立的超时配置:
{
DEFAULT: 15000, // 默认 15 秒
MIN: 5000, // 最小 5 秒
MAX: 120000, // 最大 120 秒
GEMINI_TIMEOUT: 60000 // Gemini 思考模型 60 秒
}
Gemini 系列模型(特别是带思考功能的版本)需要更长的超时时间,因此单独配置为 60 秒。
超时错误响应
错误类型与状态码
| 超时类型 | HTTP 状态码 | 错误类型 | 说明 |
|---|---|---|---|
| 首字节超时 | 524 | timeout_error | 供应商未在规定时间内返回首字节 |
| 流式空闲超时 | 524 | streaming_idle_timeout | 流式响应中断超过空闲阈值 |
| 非流式超时 | 524 | timeout_error | 非流式请求总超时 |
状态码 524 是 Cloudflare 定义的"A Timeout Occurred",表示服务器成功连接但响应超时。
错误响应格式
{
"error": {
"type": "timeout_error",
"message": "Provider failed to respond within 30000ms",
"timeout_type": "streaming_first_byte",
"timeout_ms": 30000
}
}
中文环境下的错误消息:
- 首字节超时:"供应商首字节响应超时"
- 流式空闲超时:"供应商流式响应静默超时"
熔断器集成
超时错误被归类为 PROVIDER_ERROR,会:
- 计入熔断器的失败次数统计
- 触发自动故障转移到备用供应商
- 记录在错误日志中供分析
当端点的最后一次错误是超时时,探测间隔会缩短至 10 秒,以便更快检测供应商恢复。
技术实现细节
信号组合机制
系统使用 AbortSignal.any()(或兼容 polyfill)组合多个中止信号:
- 响应超时信号:由超时计时器触发
- 客户端断开信号:由客户端连接关闭触发
- 空闲超时信号:由流式空闲检测器触发
任一信号触发都会导致请求中止,确保及时释放资源。
undici 全局配置
系统显式配置 undici 的全局调度器:
setGlobalDispatcher(
new Agent({
connectTimeout, // TCP 连接超时
headersTimeout, // 响应头超时
bodyTimeout, // 体传输超时
})
);
这是必要的,因为 undici 的默认 300 秒超时会在业务层超时之前触发,导致错误类型混淆。
故障排查
诊断超时问题
Q: 为什么请求超时了?
检查以下几个方面:
- 供应商状态:查看供应商详情页的健康状态和错误统计
- 超时配置:确认供应商的超时参数设置是否合理
- 网络连接:检查与供应商的网络连通性
- 模型负载:某些热门模型可能响应较慢
Q: 如何调整超时时间?
在供应商编辑页面修改超时参数:
- 对于响应慢的供应商,增加
firstByteTimeoutStreamingMs - 对于长文本生成场景,增加
streamingIdleTimeoutMs - 对于非流式 API 调用,调整
requestTimeoutNonStreamingMs
Q: 超时与熔断的关系?
超时错误会计入熔断器统计。如果某个供应商频繁超时,熔断器会打开并阻止向其发送请求。这是预期行为,用于保护系统免受不稳定供应商的影响。
日志分析
超时相关的日志信息包括:
timeout_type: 区分首字节超时和空闲超时timeout_ms: 配置的超时时间elapsed_ms: 实际经过的时间provider_id: 发生超时的供应商 ID
常见问题
Q: 为什么设置的超时没有生效?
可能原因:
- 环境变量未正确加载,检查
.env文件 - 供应商级配置为 0,覆盖了全局设置
- undici 的底层超时先于业务超时触发
Q: 流式响应为什么中途断开?
检查 streamingIdleTimeoutMs 设置。如果供应商的数据发送间隔超过此值,会被判定为空闲超时。对于思考型模型(如 Claude、Gemini),建议设置较大的空闲超时值。
最佳实践
根据供应商特性设置超时:不同供应商的响应时间差异较大,为每个供应商配置合适的超时参数
流式请求使用首字节超时:专注于检测响应延迟,而非限制总生成时间
合理设置空闲超时:考虑模型的思考时间和生成长度,避免正常的长响应被误判为超时
监控超时频率:频繁超时可能表明供应商不稳定或网络连接问题
配合熔断器使用:超时错误会触发熔断,确保快速故障转移
Gemini 模型特殊处理:使用 Gemini 思考模型时,适当增加 API 测试超时
