错误处理与重试
Stock SDK 内置了完善的错误处理和自动重试机制,帮助你应对网络不稳定场景。
默认行为
SDK 默认启用以下重试策略:
| 配置项 | 默认值 | 说明 |
|---|---|---|
maxRetries | 3 | 最大重试次数 |
baseDelay | 1000ms | 初始退避延迟 |
maxDelay | 30000ms | 最大退避延迟 |
backoffMultiplier | 2 | 退避系数 |
自动重试的错误类型
| 错误类型 | 是否重试 |
|---|---|
| 请求超时 | ✅ 重试 |
| 网络错误(DNS/连接失败) | ✅ 重试 |
| HTTP 408 (Request Timeout) | ✅ 重试 |
| HTTP 429 (Too Many Requests) | ✅ 重试 |
| HTTP 500/502/503/504 (服务器错误) | ✅ 重试 |
| HTTP 400/401/403/404 (客户端错误) | ❌ 不重试 |
指数退避
当请求失败时,SDK 使用指数退避策略计算等待时间:
等待时间 = baseDelay × (backoffMultiplier ^ 重试次数)示例(默认配置):
| 重试次数 | 计算 | 等待时间 |
|---|---|---|
| 第 1 次 | 1000 × 2⁰ | ~1 秒 |
| 第 2 次 | 1000 × 2¹ | ~2 秒 |
| 第 3 次 | 1000 × 2² | ~4 秒 |
这种策略给服务器恢复时间,避免在服务器过载时持续发送请求。
自定义重试配置
typescript
import { StockSDK } from 'stock-sdk';
const sdk = new StockSDK({
timeout: 10000,
retry: {
maxRetries: 5, // 最多重试 5 次
baseDelay: 500, // 初始延迟 500ms
maxDelay: 10000, // 最大延迟 10 秒
backoffMultiplier: 1.5, // 退避系数 1.5
}
});禁用重试
某些场景下你可能希望禁用自动重试:
typescript
const sdk = new StockSDK({
retry: {
maxRetries: 0 // 禁用重试
}
});重试回调
通过 onRetry 回调监听重试事件,用于日志记录或调试:
typescript
const sdk = new StockSDK({
retry: {
onRetry: (attempt, error, delay) => {
console.log(`第 ${attempt} 次重试`);
console.log(`错误: ${error.message}`);
console.log(`等待 ${Math.round(delay)}ms 后重试...`);
}
}
});细粒度控制
仅对特定错误重试
typescript
const sdk = new StockSDK({
retry: {
retryOnTimeout: true, // 超时时重试
retryOnNetworkError: false, // 网络错误不重试
retryableStatusCodes: [503, 504], // 只对这些状态码重试
}
});Provider 级策略覆盖
旧的全局 timeout / retry / rateLimit / circuitBreaker 配置仍然会作为默认策略生效。
新增的 providerPolicies 只是在指定 provider 上覆盖默认值,因此不会破坏已有初始化代码。
适用场景
- 腾讯接口保持默认速率
- 对
eastmoney单独降低请求频率 - 仅对某个 provider 开启更激进的重试或熔断
typescript
const sdk = new StockSDK({
retry: {
maxRetries: 2,
baseDelay: 500,
},
rateLimit: {
requestsPerSecond: 5,
maxBurst: 10,
},
providerPolicies: {
eastmoney: {
timeout: 12000,
retry: {
maxRetries: 5,
baseDelay: 800,
},
rateLimit: {
requestsPerSecond: 3,
maxBurst: 3,
},
circuitBreaker: {
failureThreshold: 3,
resetTimeout: 30000,
},
},
},
});可用 provider 名称
tencenteastmoneysinalinkdiaryunknown
Host Fallback 与重试预算
部分 provider(如 eastmoney)内置了多组镜像 host。当首个 host 触发可 fallback 的错误(网络错误 / 超时 / 5xx 等)时,SDK 会自动切换到下一个候选 host。
为了避免「maxRetries × host 数」造成的延迟倍乘,SDK 采用以下策略:
- 首个 host:使用配置中的完整
retry预算(默认最多 4 次:1 +maxRetries)。 - 后续 fallback host:每个仅尝试 1 次(
maxRetries强制为 0)。
因此最多请求次数 ≈ (maxRetries + 1) + (候选 host 数 - 1),在保留容灾能力的同时 避免长时间阻塞。host 不可达时也会被熔断器与 host 健康统计联合限制。
错误处理
HttpError
当服务器返回非 2xx 状态码时,SDK 抛出 HttpError:
typescript
import { StockSDK, HttpError } from 'stock-sdk';
const sdk = new StockSDK();
try {
const quotes = await sdk.getSimpleQuotes(['invalid_code']);
} catch (error) {
if (error instanceof HttpError) {
console.log(`HTTP 错误: ${error.status} ${error.statusText}`);
} else {
console.log(`其他错误: ${error.message}`);
}
}超时错误
超时错误表现为 DOMException,name 为 AbortError:
typescript
try {
const quotes = await sdk.getSimpleQuotes(['sh000001']);
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') {
console.log('请求超时');
}
}标准化错误码
如果你需要统一分类错误,但又不想失去原始 TypeError / AbortError / HttpError 实例,可以使用 getSdkErrorCode:
typescript
import { getSdkErrorCode, HttpError } from 'stock-sdk';
try {
await sdk.getSimpleQuotes(['sh000001']);
} catch (error) {
if (error instanceof HttpError) {
console.log(`HTTP 错误: ${error.status}`);
}
console.log(getSdkErrorCode(error));
// 可能返回:HTTP_ERROR / RATE_LIMITED / NETWORK_ERROR / TIMEOUT / CIRCUIT_OPEN ...
}onRetry 回调收到的仍然是原始错误实例,只是附加了标准化元数据,因此旧的错误处理代码不需要改。
配置参考
RetryOptions
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
maxRetries | number | 3 | 最大重试次数 |
baseDelay | number | 1000 | 初始退避延迟(毫秒) |
maxDelay | number | 30000 | 最大退避延迟(毫秒) |
backoffMultiplier | number | 2 | 退避系数 |
retryableStatusCodes | number[] | [408, 429, 500, 502, 503, 504] | 可重试的 HTTP 状态码 |
retryOnNetworkError | boolean | true | 是否在网络错误时重试 |
retryOnTimeout | boolean | true | 是否在超时时重试 |
onRetry | function | - | 重试回调函数 |