Skip to content

错误处理与重试

Stock SDK 内置了完善的错误处理和自动重试机制,帮助你应对网络不稳定场景。

默认行为

SDK 默认启用以下重试策略:

配置项默认值说明
maxRetries3最大重试次数
baseDelay1000ms初始退避延迟
maxDelay30000ms最大退避延迟
backoffMultiplier2退避系数

自动重试的错误类型

错误类型是否重试
请求超时✅ 重试
网络错误(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 名称

  • tencent
  • eastmoney
  • sina
  • linkdiary
  • unknown

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}`);
  }
}

超时错误

超时错误表现为 DOMExceptionnameAbortError

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

属性类型默认值说明
maxRetriesnumber3最大重试次数
baseDelaynumber1000初始退避延迟(毫秒)
maxDelaynumber30000最大退避延迟(毫秒)
backoffMultipliernumber2退避系数
retryableStatusCodesnumber[][408, 429, 500, 502, 503, 504]可重试的 HTTP 状态码
retryOnNetworkErrorbooleantrue是否在网络错误时重试
retryOnTimeoutbooleantrue是否在超时时重试
onRetryfunction-重试回调函数

Released under the ISC License.