供应商管理
供应商健康检查
供应商健康检查
供应商健康检查系统持续监控 AI 模型提供商,并自动管理其可用性。它能及早发现故障,防止连锁反应,并在供应商恢复在线时实现自动恢复。
概览
健康检查系统在多个层面运行:
- 端点级探测 - 对供应商端点进行主动 HTTP 健康检查
- 供应商级熔断器 - 基于请求的故障检测与隔离
- Agent 级健康管理 - 针对 SSL 和协议错误的连接池监控
当供应商多次失败时,系统会开启其熔断器并停止发送请求。一旦供应商恢复,熔断器会逐渐关闭并恢复流量。
熔断器状态
熔断器使用三状态模式来管理供应商健康。
状态定义
| 状态 | 描述 | 行为 |
|---|---|---|
| 关闭 (Closed) | 正常运行 | 请求正常流向供应商 |
| 开启 (Open) | 供应商异常 | 请求被阻断,快速失败 |
| 半开 (Half-open) | 测试恢复 | 允许少量请求以测试健康状况 |
状态转换
关闭 --[失败达到阈值]--> 开启 --[超时到期]--> 半开
^ |
+-------------[成功达到阈值]-------+
开启熔断器: 当连续失败达到阈值(默认:5)时,熔断器开启并阻断所有请求。
进入半开状态: 在开启持续时间(默认:30 分钟)到期后,熔断器进入半开状态,允许少量请求通过。
关闭熔断器: 当半开状态下的成功次数达到阈值(默认:2)时,熔断器关闭,恢复正常运行。
配置
熔断器设置可以通过数据库或环境变量按供应商进行自定义。
默认配置
{
failureThreshold: 5, // 开启前的失败次数
openDuration: 1800000, // 30 分钟(毫秒)
halfOpenSuccessThreshold: 2 // 关闭所需的成功次数
}
数据库结构
provider 表包含以下熔断器字段:
| 列名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
circuit_breaker_failure_threshold | integer | 5 | 开启熔断器前的失败次数 |
circuit_breaker_open_duration | integer | 1800000 | 开启持续时间(毫秒) |
circuit_breaker_half_open_success_threshold | integer | 2 | 关闭所需的成功次数 |
环境变量
通过环境变量配置智能探测系统:
| 变量 | 默认值 | 描述 |
|---|---|---|
ENABLE_SMART_PROBING | false | 启用主动恢复探测 |
PROBE_INTERVAL_MS | 10000 | 智能探测间隔(10 秒) |
PROBE_TIMEOUT_MS | 5000 | 智能探测超时(5 秒) |
端点探测
系统使用 HTTP 探测对供应商端点执行主动健康检查。
探测方法
探测采用高效的两步法:
- 首先尝试 HEAD 请求 - 更快,不需要响应体
- GET 备选 - 仅当 HEAD 返回空状态(网络/超时错误)时执行
健康判定
在以下情况下,端点被视为不健康:
- HTTP 5xx 状态码(服务器错误)
- 网络错误或超时
- DNS 解析失败
HTTP 4xx 错误 不 被视为探测失败,因为它们通常表示客户端问题,而非供应商健康问题。
探测结果结构
{
ok: boolean, // 如果 statusCode < 500 则为 true
method: "HEAD" | "GET", // 哪个方法成功了
statusCode: number | null, // HTTP 状态码或错误时的 null
latencyMs: number | null, // 响应时间
errorType: string | null, // "timeout", "network_error", "http_5xx"
errorMessage: string | null
}
错误类型
| 错误类型 | 描述 |
|---|---|
timeout | 请求超过超时限制 |
network_error | 连接失败,DNS 错误 |
invalid_url | URL 解析失败 |
http_5xx | 服务器返回 5xx 状态 |
unknown_error | 意外错误 |
探测调度器
探测调度器协调所有供应商端点的定期健康检查。
Leader 选举
在多实例部署中,只有一个实例运行探测:
- 使用基于 Redis 的分布式锁
- 锁键:
locks:endpoint-probe-scheduler - 锁 TTL:30 秒(可配置)
- 每 15 秒续期一次心跳
自适应间隔
探测频率根据端点特征进行调整:
| 场景 | 间隔 | 描述 |
|---|---|---|
| 基础 | 60 秒 | 默认探测间隔 |
| 单一供应商 | 10 分钟 | 仅有 1 个端点的供应商 |
| 最近超时 | 10 秒 | 出现过超时错误的端点 |
配置
| 变量 | 默认值 | 描述 |
|---|---|---|
ENDPOINT_PROBE_INTERVAL_MS | 60000 | 基础探测间隔 |
ENDPOINT_PROBE_TIMEOUT_MS | 5000 | 请求超时时间 |
ENDPOINT_PROBE_CONCURRENCY | 10 | 并发探测工作线程数 |
ENDPOINT_PROBE_CYCLE_JITTER_MS | 1000 | 每个周期的随机延迟 |
ENDPOINT_PROBE_LOCK_TTL_MS | 30000 | Leader 锁 TTL |
智能恢复探测
当供应商熔断器开启时,智能探测系统通过定期使用实际 API 调用测试供应商,加速恢复检测。
工作原理
- 检测 - 识别处于开启 (OPEN) 状态的供应商
- 测试 - 发送实际的补全请求以验证 API 功能
- 转换 - 成功后,立即将熔断器移至半开 (HALF-OPEN) 状态
- 验证 - 真实流量逐渐确认恢复
这比 HTTP 探测更全面,因为它验证了供应商 API 是否真正能处理 AI 模型请求,而不只是端点响应 HTTP。
测试执行
测试服务使用以下参数执行补全测试:
{
providerUrl: string, // 供应商基础 URL
apiKey: string, // 认证密钥
providerType: string, // 决定请求格式
model?: string, // 要测试的模型
latencyThresholdMs: 5000, // 延迟阈值
timeoutMs: 10000 // 请求超时时间
}
测试结果
| 字段 | 描述 |
|---|---|
success | 整体测试是否通过 |
status | "green", "yellow", 或 "red" |
subStatus | 详细状态,如 "slow_latency", "auth_error" |
latencyMs | 总请求延迟 |
firstByteMs | 首字节时间 |
content | 响应预览(已截断) |
可用性监控
可用性服务根据实际请求历史计算供应商健康状况。
状态分类
请求根据结果进行分类:
| 状态 | 条件 | 含义 |
|---|---|---|
| 绿色 (Green) | 2xx/3xx 状态码 | 成功 |
| 红色 (Red) | 4xx/5xx 状态码或 null | 错误 |
| 未知 (Unknown) | 无数据 | 历史记录不足 |
当前状态计算
系统分析最后 3 个时间段(近期历史)以确定当前状态:
- 如果无可用数据,则为 未知(不假设为健康)
- 如果成功率 >= 50%,则为 绿色
- 如果成功率 < 50%,则为 红色
可用性评分
score = greenCount / (greenCount + redCount)
1.0 的评分表示 100% 可用,0.0 表示完全故障。
供应商选择集成
健康检查系统与供应商选择集成,确保请求只发送到健康的供应商。
选择流水线
在路由请求时,供应商会经过多个过滤阶段:
- 分组预过滤 - 按用户/密钥供应商组过滤
- 基础过滤 - 检查启用状态、格式兼容性、模型支持
- 1M 上下文过滤 - 过滤掉禁用上下文偏好的供应商
- 健康检查 - 熔断器 + 消费限制
- 优先级分层 - 选择优先级最高的供应商
- 成本排序 + 加权选择 - 按成本排序,进行加权随机选择
健康过滤
filterByLimits 函数检查:
- 厂商类型熔断器 - 检查厂商和类型组合是否被阻断
- 供应商熔断器 - 检查单个供应商的熔断器状态
- 消费限制 - 验证 5 小时、每日、每周、每月的成本限制
- 总成本限制 - 检查累计消费限制
并发会话限制在原子操作中单独检查,以避免竞态条件。
决策上下文
系统跟踪每个阶段过滤掉了多少供应商:
context.beforeHealthCheck = candidateProviders.length;
const healthyProviders = await filterByLimits(candidateProviders);
context.afterHealthCheck = healthyProviders.length;
这使得日志中能够显示决策链可视化。
自动故障转移
当供应商在请求处理过程中发生故障时,系统会自动处理故障转移。
故障处理流程
- 记录故障 - 增加该供应商的失败计数
- 检查阈值 - 如果达到阈值,开启熔断器
- 触发告警 - 发送 Webhook 通知(如果已配置)
- 重试备选 - 请求可以使用链中的下一个供应商进行重试
恢复流程
当恢复中的供应商被成功探测时:
- 智能探测成功 - 转换到半开 (HALF-OPEN) 状态
- 限制流量 - 逐渐使用真实请求进行测试
- 成功跟踪 - 统计连续成功次数
- 完全恢复 - 在达到阈值成功次数后,关闭熔断器
告警通知
当熔断器开启时,系统可以发送 Webhook 通知以提醒管理员。
告警内容
通知包含:
- 供应商名称和 ID
- 触发开启的失败计数
- 将尝试重试的时间戳
- 最后一条错误信息
重复抑制
告警会在 Redis 中缓存 5 分钟,以防止针对同一供应商发送重复通知。
配置
支持两种通知模式:
- 传统模式 - 单个 Webhook URL
- 基于绑定的模式 - 带有供应商绑定的多个 Webhook 目标
在 settings.enabled 和 settings.circuitBreakerEnabled 中检查设置。
Agent 池健康
Agent 池独立于熔断器管理 HTTP 连接健康。
不健康 Agent 检测
在以下情况下,Agent 被标记为不健康:
- 检测到 SSL 证书错误
- 检测到 HTTP/2 协议错误
替换流程
- 在请求转发中检测到错误
- Agent 被标记为不健康并附带原因
- 在下一个请求时,不健康的 Agent 被剔除
- 为该请求创建新的 Agent
这可以处理连接层的问题,而不影响供应商的熔断器状态。
错误模式
SSL 错误: certificate, ssl, tls, cert_, self signed, hostname mismatch, unable_to_get_issuer_cert, cert_has_expired
HTTP/2 错误: GOAWAY, RST_STREAM, PROTOCOL_ERROR, ERR_HTTP2_, NGHTTP2_, HTTP_1_1_REQUIRED, REFUSED_STREAM
健康检查端点
基础健康端点
GET /api/actions/health
响应:
{
"status": "ok",
"timestamp": "2025-01-29T12:00:00.000Z",
"version": "1.0.0"
}
由 Docker 健康检查和负载均衡器使用。
供应商健康状态 API
Action: getProvidersHealthStatus
仅限管理员的操作,返回所有供应商的熔断器状态:
Record<number, {
circuitState: "closed" | "open" | "half-open";
failureCount: number;
lastFailureTime: number | null;
circuitOpenUntil: number | null;
recoveryMinutes: number | null;
}>
可用性查询 API
GET /api/availability
查询参数:
| 参数 | 默认值 | 描述 |
|---|---|---|
startTime | 24 小时前 | 时间范围起点 (ISO 字符串) |
endTime | 现在 | 时间范围终点 (ISO 字符串) |
providerIds | 全部 | 以逗号分隔的供应商 ID |
bucketSizeMinutes | 变化 | 时间段大小(最小 0.25 分钟) |
includeDisabled | false | 包含已禁用的供应商 |
maxBuckets | 100 | 最大时间段数量 |
当前状态 API
GET /api/availability/current
根据最后 15 分钟的数据,返回所有已启用供应商的轻量级当前状态。
仪表盘可视化
供应商设置页面
健康状态在供应商列表中以徽章形式显示:
- 熔断器开启 - 带有恢复倒计时的红色徽章,手动重置按钮
- 半开 - 表示恢复进行中的黄色徽章
- 无徽章 - 关闭(健康)
管理员可以通过确认对话框手动重置熔断器。
可用性仪表盘
功能包括:
- 摘要卡片 - 系统可用性,健康/不健康/未知计数
- 时间范围选择 - 15 分钟, 1 小时, 6 小时, 24 小时, 7 天
- 热力图可视化 - 随时间变化的可用性
颜色编码:
| 颜色 | 可用性 | 含义 |
|---|---|---|
| 绿色 | >= 95% | 极好 |
| 酸橙色 | 80-95% | 良好 |
| 橙色 | 50-80% | 降级 |
| 红色 | < 50% | 差 |
| 灰色 | 无数据 | 未知 |
提示框信息:
- 时间段
- 总请求数
- 可用性百分比
- 平均延迟
- 绿色/红色请求计数
状态持久化
熔断器使用双层持久化策略。
内存层(主要)
- 请求处理期间快速访问
- 无网络延迟
- 进程重启时丢失
Redis 层(备份)
- 跨实例共享
- 进程重启后依然存在
- 状态键具有 24 小时 TTL
- 配置键无 TTL(永久)
同步策略
- 直写 (Write-through) - 每次状态更改立即写入 Redis
- 延迟读取 (Lazy read) - 仅当需要状态且内存中不存在时检查 Redis
- 批量加载 - 为管理员查询并行加载所有状态
Redis 键结构
熔断器状态:
键名: circuit_breaker:state:{providerId}
类型: Hash
TTL: 86400 秒 (24 小时)
熔断器配置:
键名: circuit_breaker:config:{providerId}
类型: Hash
TTL: 无 (永久)
边缘情况与韧性
Redis 不可用
当 Redis 不可用时:
- 熔断器以仅内存模式运行
- 状态不跨实例共享
- 重启后状态丢失
- 缓存未命中时配置回退到数据库
Redis 客户端使用 enableOfflineQueue: false 以实现快速失败和指数退避重试策略。
多实例协调
- Leader 选举确保只有一个实例运行探测
- 熔断器状态在更改时立即持久化到 Redis
- 启动时批量加载以提高效率
无数据场景
- 当不存在请求数据时,可用性状态为 "未知" (而非 "绿色")
- 新供应商从关闭 (CLOSED) 状态开始,失败次数为零
- 这避免了错误地报告健康状态
超时处理
- 探测超时默认为 5 秒
- 出现超时错误的端点探测频率更高(10 秒间隔)
- 请求超时不触发熔断器(仅失败触发)
供应商删除清理
当供应商被删除时:
- 从
healthMap中清除内存状态 - 删除 Redis 键
- 不留任何过期状态
最佳实践
调整熔断器阈值
| 场景 | 建议 |
|---|---|
| 不稳定的供应商 | 将 failureThreshold 降至 3,增加 openDuration |
| 可靠的供应商 | 保持默认值或将 failureThreshold 增加到 10 |
| 关键供应商 | 将 halfOpenSuccessThreshold 降至 1 以实现更快恢复 |
| 测试环境 | 减少所有超时时间以获得更快反馈 |
监控健康检查指标
要跟踪的关键指标:
- 熔断器状态分布(关闭/开启/半开计数)
- 各端点的探测成功/失败率
- 平均探测延迟
- 熔断器开启后的恢复时间
处理供应商维护
对供应商进行维护时:
- 在设置中禁用该供应商(防止新请求)
- 等待活动会话完成
- 执行维护
- 重新启用供应商
- 监控健康状态以确认恢复
或者,如果在维护期间触发了熔断器,维护完成后手动重置熔断器。
故障排除
熔断器意外开启
检查:
- 日志中近期的供应商错误
- 网络连通性问题
- 认证失败(API 密钥过期)
- 供应商的频率限制
供应商卡在开启状态
可能的原因:
- 供应商确实宕机(通过直接 API 测试验证)
- 智能探测已禁用 (
ENABLE_SMART_PROBING=false) - 探测超时时间对于慢速供应商来说太短
- Redis 连接问题阻止了状态同步
健康检查误报
如果探测通过但请求失败:
- 探测使用 HEAD 请求,实际请求可能命中不同的端点
- 检查供应商是否具有不同健康状况的多个端点
- 如果某些模型失败,请查看特定模型的可用性
探测延迟高
如果探测缓慢:
- 增加
ENDPOINT_PROBE_INTERVAL_MS - 检查到供应商的网络路径
- 考虑与供应商端点的地理距离
- 检查单一供应商间隔(10 分钟)是否合适
