独家合作CubenceAI 中转平台立减 10%访问

代理功能

超时控制

超时控制是 Claude Code Hub 代理服务的重要组成部分,用于管理 LLM API 请求的生命周期。系统实现了多层超时机制,确保请求不会无限期挂起,同时支持流式响应的空闲检测。

为什么需要超时控制

在与 LLM 供应商交互时,可能遇到以下问题:

  • 响应延迟:某些供应商可能由于负载过高而响应缓慢
  • 连接挂起:网络问题导致请求长时间无响应
  • 流式中断:流式响应中途停止发送数据
  • 资源占用:长时间运行的请求占用连接池和内存

超时控制通过设置合理的等待时间上限,确保系统能够快速检测问题并触发故障转移。

双路径超时机制

系统根据请求类型采用不同的超时策略:

请求类型超时参数作用
流式请求firstByteTimeoutStreamingMs控制等待首字节响应的时间
非流式请求requestTimeoutNonStreamingMs控制整个请求的总超时时间

流式请求超时

流式请求使用首字节超时机制:

  1. 建立连接后,等待供应商返回第一个数据块
  2. 如果在 firstByteTimeoutStreamingMs 内未收到首字节,触发超时
  3. 收到首字节后,切换到空闲检测模式

这种设计允许快速识别响应缓慢的供应商,同时不限制长文本生成的总时长。

非流式请求超时

非流式请求使用总超时机制:

  1. 从发送请求开始计时
  2. 如果在 requestTimeoutNonStreamingMs 内未完成响应,触发超时
  3. 适用于一次性返回完整响应的场景

流式空闲检测

对于流式响应,系统实现了空闲超时检测(静默期监控):

空闲检测原理

空闲检测器监控数据流的接收间隔。每当收到新的数据块,计时器重置;如果在 streamingIdleTimeoutMs 内未收到任何数据,判定为空闲超时。

超时触发动作

当流式空闲超时发生时,系统执行以下操作:

  1. 关闭客户端流:向客户端发送错误通知,结束流式连接
  2. 中止上游连接:终止与供应商的连接,防止资源泄漏
  3. 停止后台任务:取消正在进行的读取任务

客户端断开处理

当客户端主动断开连接时:

  • 立即清除空闲计时器
  • 取消后台读取任务
  • 上游连接自然结束(不强制中断)

这种设计避免不必要的资源浪费,同时允许供应商完成其内部处理。

配置参数

供应商级超时配置

每个供应商可以独立配置超时参数,存储在数据库中:

参数字段名默认值范围说明
流式首字节超时firstByteTimeoutStreamingMs01000-180000 ms等待首字节的时间,0 表示无限制
流式空闲超时streamingIdleTimeoutMs060000-600000 ms流式响应的空闲检测时间,0 表示无限制
非流式请求超时requestTimeoutNonStreamingMs060000-1800000 ms非流式请求的总超时时间,0 表示无限制

零值含义

将超时参数设置为 0 会禁用对应的超时检测(相当于无限等待)。建议为生产环境的供应商设置合理的超时值。

全局环境变量

系统级别的底层超时配置:

变量名默认值说明
FETCH_CONNECT_TIMEOUT30000TCP 连接超时(毫秒),包括 DNS 解析和 TLS 握手
FETCH_HEADERS_TIMEOUT600000等待响应头/首字节超时(毫秒)
FETCH_BODY_TIMEOUT600000请求/响应体传输超时(毫秒)
API_TEST_TIMEOUT_MS15000API 测试请求超时(毫秒),范围 5000-120000

这些环境变量控制底层 HTTP 客户端(undici)的行为,覆盖其默认的 300 秒超时限制。

API 测试特殊处理

API 测试使用独立的超时配置:

{
  DEFAULT: 15000,      // 默认 15 秒
  MIN: 5000,           // 最小 5 秒
  MAX: 120000,         // 最大 120 秒
  GEMINI_TIMEOUT: 60000 // Gemini 思考模型 60 秒
}

Gemini 系列模型(特别是带思考功能的版本)需要更长的超时时间,因此单独配置为 60 秒。

超时错误响应

错误类型与状态码

超时类型HTTP 状态码错误类型说明
首字节超时524timeout_error供应商未在规定时间内返回首字节
流式空闲超时524streaming_idle_timeout流式响应中断超过空闲阈值
非流式超时524timeout_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: 为什么请求超时了?

检查以下几个方面:

  1. 供应商状态:查看供应商详情页的健康状态和错误统计
  2. 超时配置:确认供应商的超时参数设置是否合理
  3. 网络连接:检查与供应商的网络连通性
  4. 模型负载:某些热门模型可能响应较慢

Q: 如何调整超时时间?

在供应商编辑页面修改超时参数:

  • 对于响应慢的供应商,增加 firstByteTimeoutStreamingMs
  • 对于长文本生成场景,增加 streamingIdleTimeoutMs
  • 对于非流式 API 调用,调整 requestTimeoutNonStreamingMs

Q: 超时与熔断的关系?

超时错误会计入熔断器统计。如果某个供应商频繁超时,熔断器会打开并阻止向其发送请求。这是预期行为,用于保护系统免受不稳定供应商的影响。

日志分析

超时相关的日志信息包括:

  • timeout_type: 区分首字节超时和空闲超时
  • timeout_ms: 配置的超时时间
  • elapsed_ms: 实际经过的时间
  • provider_id: 发生超时的供应商 ID

常见问题

Q: 为什么设置的超时没有生效?

可能原因:

  1. 环境变量未正确加载,检查 .env 文件
  2. 供应商级配置为 0,覆盖了全局设置
  3. undici 的底层超时先于业务超时触发

Q: 流式响应为什么中途断开?

检查 streamingIdleTimeoutMs 设置。如果供应商的数据发送间隔超过此值,会被判定为空闲超时。对于思考型模型(如 Claude、Gemini),建议设置较大的空闲超时值。

最佳实践

  1. 根据供应商特性设置超时:不同供应商的响应时间差异较大,为每个供应商配置合适的超时参数

  2. 流式请求使用首字节超时:专注于检测响应延迟,而非限制总生成时间

  3. 合理设置空闲超时:考虑模型的思考时间和生成长度,避免正常的长响应被误判为超时

  4. 监控超时频率:频繁超时可能表明供应商不稳定或网络连接问题

  5. 配合熔断器使用:超时错误会触发熔断,确保快速故障转移

  6. Gemini 模型特殊处理:使用 Gemini 思考模型时,适当增加 API 测试超时

相关文档

Previous
限流