Node.js 独立产品 API 限流与降级:保护后端不被流量冲垮

📅 2026/7/10 17:13:26
Node.js 独立产品 API 限流与降级:保护后端不被流量冲垮
Node.js 独立产品 API 限流与降级保护后端不被流量冲垮一、流量洪峰下的服务雪崩为什么限流和降级是独立产品的生存底线独立产品的后端资源通常有限无法像大企业那样通过横向扩展应对流量峰值。一次意外的流量洪峰如被大 V 转发、被爬虫集中访问、遭遇恶意攻击就可能导致服务完全不可用。更危险的是服务雪崩效应一个接口响应变慢会占用更多连接和线程导致其他接口也受到影响最终整个应用崩溃。API 限流Rate Limiting通过控制单位时间内的请求数量保护系统资源。服务降级Degradation在高负载时主动关闭非核心功能确保核心业务可用。两者结合构建了后端服务的弹性防线。graph TD A[客户端请求] -- B[限流中间件] B --|通过| C[负载监控] B --|拒绝| D[返回 429br/Too Many Requests] C --|负载正常| E[正常处理] C --|负载过高| F[触发降级] F -- G[关闭非核心功能] F -- H[返回缓存/默认结果] E -- I[业务处理] I -- J[返回结果] G -- J H -- J二、限流算法的工程实现与选型常见的限流算法包括固定窗口、滑动窗口、令牌桶和漏桶。每种算法在精度和性能上有不同的权衡。2.1 令牌桶算法实现推荐令牌桶算法允许一定程度的突发流量同时长期平均速率受限。是实现 API 限流的常用选择。/** * 基于 Redis 的分布式令牌桶限流 * 支持多实例部署场景 */ import { createClient } from redis/client; import { RateLimiterRedis } from rate-limiter-flexible; class TokenBucketRateLimiter { constructor(redisClient, options {}) { this.redis redisClient; // 限流配置 this.points options.points || 100; // 令牌桶容量 this.duration options.duration || 60; // 时间窗口秒 this.blockDuration options.blockDuration || 60; // 超限后的封禁时间秒 // 初始化 rate-limiter-flexible this.limiter new RateLimiterRedis({ storeClient: redisClient, points: this.points, duration: this.duration, blockDuration: this.blockDuration, keyPrefix: rl // Redis key 前缀 }); } /** * Express 限流中间件工厂 * param {Object} options - 限流选项 * returns {Function} Express 中间件 */ middleware(options {}) { const { points, // 覆盖默认点数 duration, // 覆盖默认时间窗口 keyGenerator, // 自定义 key 生成函数 onRejected // 拒绝请求的回调函数 } options; return async (req, res, next) { try { // 1. 生成限流 key const key keyGenerator ? keyGenerator(req) : this.defaultKeyGenerator(req); // 2. 执行限流检查 const rateLimiter points || duration ? new RateLimiterRedis({ storeClient: this.redis, points: points || this.points, duration: duration || this.duration, blockDuration: this.blockDuration, keyPrefix: rl }) : this.limiter; const rateLimitResult await rateLimiter.consume(key); // 3. 在响应头中返回限流信息 res.setHeader(X-RateLimit-Limit, this.points); res.setHeader(X-RateLimit-Remaining, rateLimitResult.remainingPoints); res.setHeader(X-RateLimit-Reset, new Date(Date.now() rateLimitResult.msBeforeNext).toISOString()); next(); } catch (rateLimiterRes) { // 限流触发请求被拒绝 const secsBeforeNext Math.ceil(rateLimiterRes.msBeforeNext / 1000) || 1; res.setHeader(Retry-After, String(secsBeforeNext)); res.setHeader(X-RateLimit-Limit, this.points); res.setHeader(X-RateLimit-Remaining, 0); res.setHeader(X-RateLimit-Reset, new Date(Date.now() rateLimiterRes.msBeforeNext).toISOString()); if (onRejected) { onRejected(req, res, rateLimiterRes); } else { res.status(429).json({ error: Too Many Requests, message: 请求过于频繁请在 ${secsBeforeNext} 秒后重试, retryAfter: secsBeforeNext }); } } }; } /** * 默认 key 生成基于 IP 地址 */ defaultKeyGenerator(req) { // 优先使用 X-Forwarded-For 的第一个 IP经过代理时 const forwarded req.headers[x-forwarded-for]; const ip forwarded ? forwarded.split(,)[0].trim() : req.ip || req.connection.remoteAddress; return ip:${ip}; } /** * 基于用户的限流 key需要认证 */ userKeyGenerator(req) { const userId req.user?.id || anonymous; return user:${userId}; } /** * 基于 API Key 的限流 */ apiKeyGenerator(req) { const apiKey req.headers[x-api-key] || req.query.apiKey; return apiKey ? apikey:${apiKey} : this.defaultKeyGenerator(req); } } export default TokenBucketRateLimiter;2.2 在不同场景下使用不同的限流策略/** * 限流策略配置示例 */ import express from express; import TokenBucketRateLimiter from ./middleware/rate-limiter.js; const app express(); const redis createClient({ url: process.env.REDIS_URL }); const limiter new TokenBucketRateLimiter(redis); // 1. 全局限流每个 IP 每分钟最多 100 次请求 app.use(limiter.middleware({ points: 100, duration: 60 })); // 2. 认证用户限流登录用户有更高配额 app.use(/api, limiter.middleware({ points: 1000, duration: 60, keyGenerator: (req) { if (req.user) { return user:${req.user.id}; } return ip:${req.ip}; } })); // 3. 敏感接口限流登录接口更严格的限制 app.post(/auth/login, limiter.middleware({ points: 5, // 每分钟最多 5 次 duration: 60, keyGenerator: (req) login:${req.ip} }), async (req, res) { // 登录逻辑 } ); // 4. 付费用户限流基于订阅等级 app.use(/api/pro, limiter.middleware({ points: (req) { const tier req.user?.subscriptionTier || free; const tierLimits { free: 100, pro: 1000, enterprise: 10000 }; return tierLimits[tier] || 100; }, duration: 60, keyGenerator: (req) user:${req.user?.id || anon} })); app.listen(3000);三、服务降级策略保障核心业务的可用性当系统负载超过阈值时需要主动降级非核心功能。降级可以是自动触发基于监控指标或手动触发运维操作。3.0 场景分级降级与恢复风暴降级必须是分级的——不能一刀切全部关掉。例如独立电商产品的秒杀场景中第1级负载 70%关闭分析、推荐——用户完全无感第2级负载 85%搜索降级为缓存模式、评论只读第3级负载 95%通知转为延迟队列第4级负载 99%只保留下单和支付核心路径const GRADUAL_DEGRADATION_PLAN [ { load: 0.70, features: [analytics, recommendation] }, { load: 0.85, features: [search, comments] }, { load: 0.95, features: [notifications] }, { load: 0.99, features: [everything_except_core] }, ];踩坑降级后的恢复风暴当系统负载恢复正常后所有功能同时恢复会引发新的尖峰——恢复风暴。恢复必须采用逐步解禁每个功能恢复间隔 30 秒并设置 5 分钟冷静期避免震荡。class DegradationRecoveryManager { constructor() { this.cooldowns new Map(); this.isRecovering false; } async startGradualRecovery() { if (this.isRecovering) return; this.isRecovering true; const degradedFeatures await this.getCurrentlyDegradedFeatures(); for (const feature of degradedFeatures) { const cooldownUntil this.cooldowns.get(feature) || 0; if (Date.now() cooldownUntil) { console.log(${feature} 处于冷静期跳过); continue; } await this.dm.disableDegradation(feature); // 5 分钟冷静期 this.cooldowns.set(feature, Date.now() 5 * 60 * 1000); console.log(${feature} 已恢复30s后恢复下一个); await new Promise(r setTimeout(r, 30000)); } this.isRecovering false; } }3.1 降级开关管理/** * 服务降级管理器 * 管理各个功能的降级开关 */ import { createClient } from redis/client; class DegradationManager { constructor(redisClient) { this.redis redisClient; this.switches new Map(); // 本地缓存的开关状态 } /** * 定义可降级的功能 */ getDegradableFeatures() { return { // 搜索功能降级后返回缓存结果或禁用 search: { name: 搜索功能, level: non_critical, fallback: cache }, // 推荐功能降级后返回默认推荐 recommendation: { name: 推荐算法, level: non_critical, fallback: default }, // 评论功能降级后只读不允许新评论 comments: { name: 评论功能, level: semi_critical, fallback: read_only }, // 通知功能降级后延迟发送 notifications: { name: 实时通知, level: non_critical, fallback: delay }, // 分析功能降级后停止收集 analytics: { name: 用户行为分析, level: optional, fallback: disable } }; } /** * 开启降级手动或自动触发 * param {string} feature - 功能名称 * param {string} reason - 降级原因 * param {number} duration - 降级持续时间秒0 表示手动恢复 */ async enableDegradation(feature, reason, duration 0) { const key degradation:${feature}; const degradationInfo { enabled: true, reason, enabledAt: new Date().toISOString(), expiresAt: duration 0 ? new Date(Date.now() duration * 1000).toISOString() : null }; await this.redis.set(key, JSON.stringify(degradationInfo)); // 更新本地缓存 this.switches.set(feature, degradationInfo); console.warn(功能降级已开启: ${feature}, 原因: ${reason}); } /** * 关闭降级 */ async disableDegradation(feature) { const key degradation:${feature}; await this.redis.del(key); this.switches.delete(feature); console.info(功能降级已关闭: ${feature}); } /** * 检查功能是否降级 */ async isDegraded(feature) { // 1. 检查本地缓存 if (this.switches.has(feature)) { const info this.switches.get(feature); if (info.expiresAt new Date(info.expiresAt) new Date()) { // 已过期自动恢复 await this.disableDegradation(feature); return false; } return info.enabled; } // 2. 从 Redis 加载 const key degradation:${feature}; const data await this.redis.get(key); if (!data) { return false; } const info JSON.parse(data); // 检查是否过期 if (info.expiresAt new Date(info.expiresAt) new Date()) { await this.disableDegradation(feature); return false; } this.switches.set(feature, info); return info.enabled; } }3.2 自动降级触发器基于系统指标CPU、内存、响应时间自动触发降级/** * 自动降级监控器 * 基于系统负载自动触发降级 */ class AutoDegradationMonitor { constructor(degradationManager, options {}) { this.dm degradationManager; this.thresholds { cpu: options.cpuThreshold || 80, // CPU 使用率阈值% memory: options.memThreshold || 85, // 内存使用率阈值% responseTime: options.rtThreshold || 1000, // 平均响应时间阈值ms errorRate: options.errThreshold || 0.05 // 错误率阈值0-1 }; this.checkInterval options.checkInterval || 10000; // 检查间隔ms this.monitoring false; } start() { if (this.monitoring) return; this.monitoring true; this.timer setInterval(() this.checkAndDegrade(), this.checkInterval); console.log(✅ 自动降级监控已启动); } stop() { this.monitoring false; if (this.timer) { clearInterval(this.timer); } } async checkAndDegrade() { try { const metrics await this.collectMetrics(); // 检查 CPU if (metrics.cpu this.thresholds.cpu) { await this.triggerDegradation(high_cpu, metrics); } // 检查内存 if (metrics.memory this.thresholds.memory) { await this.triggerDegradation(high_memory, metrics); } // 检查响应时间 if (metrics.avgResponseTime this.thresholds.responseTime) { await this.triggerDegradation(slow_response, metrics); } // 检查错误率 if (metrics.errorRate this.thresholds.errorRate) { await this.triggerDegradation(high_error_rate, metrics); } } catch (error) { console.error(自动降级检查失败:, error); } } async collectMetrics() { const memUsage process.memoryUsage(); const cpuUsage process.cpuUsage(); return { cpu: this.getCpuPercent(cpuUsage), memory: (memUsage.heapUsed / memUsage.heapTotal) * 100, avgResponseTime: await this.getAvgResponseTime(), errorRate: await this.getErrorRate() }; } async triggerDegradation(trigger, metrics) { console.warn(触发自动降级: ${trigger}, metrics); // 根据触发类型降级不同功能 const degradationPlan { high_cpu: [recommendation, analytics], high_memory: [search, analytics], slow_response: [comments, notifications], high_error_rate: [recommendation, notifications] }; const features degradationPlan[trigger] || [analytics]; for (const feature of features) { const isAlreadyDegraded await this.dm.isDegraded(feature); if (!isAlreadyDegraded) { await this.dm.enableDegradation( feature, 自动触发: ${trigger}, 300 // 5分钟后自动尝试恢复 ); } } } getCpuPercent(cpuUsage) { // 简化实现实际应使用 pidusage 等库 return 0; } async getAvgResponseTime() { // 实际实现中从 APM 或日志中计算 return 0; } async getErrorRate() { // 实际实现中计算最近时间窗口的错误率 return 0; } }3.3 在业务中使用降级/** * 使用降级的功能示例 */ import express from express; const app express(); const dm new DegradationManager(redis); // 搜索接口支持降级 app.get(/api/search, async (req, res) { const isSearchDegraded await dm.isDegraded(search); if (isSearchDegraded) { // 降级策略返回缓存的热门搜索结果 const cachedResults await getCachedSearchResults(req.query.q); return res.json({ results: cachedResults, degraded: true, message: 搜索功能暂时降级返回缓存结果 }); } // 正常搜索逻辑 const results await performSearch(req.query.q); res.json({ results }); }); // 推荐接口支持降级 app.get(/api/recommendations, async (req, res) { const isRecDegraded await dm.isDegraded(recommendation); if (isRecDegraded) { // 降级策略返回默认的热门内容 const defaultRecs await getDefaultRecommendations(); return res.json({ recommendations: defaultRecs, degraded: true }); } const recs await generatePersonalizedRecommendations(req.user.id); res.json({ recommendations: recs }); }); // 评论接口支持降级只读模式 app.post(/api/posts/:id/comments, async (req, res) { const isCommentDegraded await dm.isDegraded(comments); if (isCommentDegraded) { return res.status(503).json({ error: 评论功能暂时维护中, degraded: true }); } const comment await createComment(req.params.id, req.body); res.json(comment); });3.4 降级策略的最佳实践在设计降级策略时应该遵循用户体验优先原则。降级 mode 应该尽可能返回有用的内容而不是直接返回错误。例如搜索功能降级时可以返回热门内容而非清空结果推荐功能降级时返回编辑精选内容而非空白。同时建议在响应头中添加X-Degraded: feature-name标记方便前端识别当前服务状态并向用户展示适当的提示信息。四、限流与降级的边界条件和监控限流和降级都不是银弹。错误的配置可能导致合法用户无法访问或者降级范围过大影响用户体验。4.1 限流的边界条件分布式环境下的时钟同步使用 Redis 作为集中式限流存储可以避免多实例之间的计数不一致。限流 key 的设计仅基于 IP 限流容易被 NAT 环境下的多个用户共享限制仅基于用户 ID 限流则匿名用户无限制。突发流量的处理令牌桶允许突发但突发量需要合理设置通常为桶容量的 1.5 倍。4.2 监控与告警/** * 限流和降级事件监控 */ class RateLimitMonitor { constructor(redisClient) { this.redis redisClient; } /** * 记录限流事件 */ async recordLimitEvent(key, remaining, msBeforeNext) { const event { key, remaining, msBeforeNext, timestamp: Date.now() }; await this.redis.lPush( ratelimit:events, JSON.stringify(event) ); // 保留最近 1000 条 await this.redis.lTrim(ratelimit:events, 0, 999); // 如果剩余点数过低触发告警 if (remaining 10) { this.triggerAlert(rate_limit_low, { key, remaining }); } } /** * 获取限流统计 */ async getLimitStats(timeWindow 3600) { const events await this.redis.lRange(ratelimit:events, 0, -1); const parsed events.map(e JSON.parse(e)); const now Date.now(); const recent parsed.filter(e (now - e.timestamp) timeWindow * 1000); return { totalLimited: recent.length, topLimitedKeys: this.getTopKeys(recent), avgRemaining: recent.reduce((sum, e) sum e.remaining, 0) / recent.length }; } }五、总结API 限流和服务降级是保护独立产品后端稳定性的核心手段。限流通过控制请求速率防止资源耗尽降级通过牺牲非核心功能保障核心业务可用。工程实施中需重点关注分布式限流的一致性、降级触发的准确性和恢复机制的可靠性。落地路线确定核心与非核心功能边界 → 实现分布式限流中间件 → 建立降级开关系统 → 配置自动降级触发规则 → 建立限流和降级事件监控告警。