网络抓取的速率限制最佳实践

学习限制速率的最佳实践,以负责任的方式刮擦网站,而不会触发阻止或使目标服务器不堪重负。
Rate limiting best practices

当我们使用微服务和分布式系统构建现代应用程序时,我们很快就会意识到,流量控制不再是一个可有可无的东西。它是保证一切运行的核心部分。以前,限制速率只是阻止过多请求的一种简单方法。现在,它在我们如何保护 API、保持 API 快速运行以及提供流畅的用户体验方面发挥着更重要的作用。由于 API 几乎是所有数字产品的核心,因此如何管理请求流将决定我们的系统是保持稳定,还是在峰值情况下出现故障。

在本指南中,我们将介绍速率限制如何在实际系统中发挥作用。我们将介绍设计它的实用方法、如何将它插入到你的架构中,以及团队如何在生产中使用它来构建能承受压力而不崩溃的系统。

值得借鉴的 10 项 API 费率限制最佳实践

应用程序接口需要防止滥用和超载。良好的速率限制可保持系统的可靠性,并对每个人都公平。以下是帮助您有效管理 API 速率限制的十项最佳实践。

1.了解您的流量

有效限制速率的基础是深入了解 API 的流量。分析几小时、几天和几个月的使用情况。什么是高峰时段?什么时候流量激增最多?是否有特定的端点吸引了更多的关注,或者受到自动化或刮擦的影响?使用监控工具–无论是 AWS CloudWatch 这样的云原生工具、Datadog 这样的第三方解决方案,还是 Prometheus 这样的开源项目–进行跟踪:

  • 每个时间窗口(秒、分、小时、天)的请求总数
  • 按应用程序接口密钥或用户申请费率
  • 按端点分列的请求分布情况
  • 地理或时间趋势
  • 与新功能发布或营销活动相关的模式

例如,来自单一 IP 或 API 密钥的请求突然激增可能表明发生了 DDoS 攻击,而有规律、可预测的激增则可能是由业务运营驱动的。有了这一基线,您就有能力设计出既能严格保护服务,又能灵活满足真正用户需求的限制。

2.选择正确的速率限制算法

您选择的速率限制算法决定了简单性、资源使用和用户体验之间的平衡。最广泛采用的策略有

固定窗

这是最简单的方法:在设定的时间窗口(例如每分钟 60 个请求)内的所有请求都会被计数,计数器会在每个新窗口开始时重置。这种方法虽然容易实现,但容易产生 “边界 “效应,即用户可以在一个窗口结束和下一个窗口开始时发送突发请求。下面是使用字典计数器的 Python 快速实现方法:

导入时间

WINDOW_SIZE = 60 # 秒

max_requests = 60

请求计数器 = {}

def allow_request(user_id):

current_window = int(time.time()) // WINDOW_SIZE

key = (user_id, current_window)

count = request_counters.get(key, 0)

如果 count < MAX_REQUESTS:

request_counters[key] = count + 1

返回 True

返回 False

推拉窗

为避免固定窗口的突发性,滑动窗口算法通过跟踪过去 “n “秒内的请求(无论边界如何)来平滑限制。这通常是通过队列或列表中每个用户的时间戳来实现的:

from collections import deque

导入时间

window_size = 60

max_requests = 60

user_requests = {}

def allow_request(user_id):

now = time.time()

queue = user_requests.setdefault(user_id, deque())

当 queue 和 queue[0] < now – WINDOW_SIZE 时:

queue.popleft()

如果 len(queue) < MAX_REQUESTS:

queue.append(now)

返回 True

返回 False

代币桶

令牌桶是一种处理突发流量的灵活方法,只要 “令牌 “可用,就允许请求。令牌以设定的速度重新填充,直至最大容量。如果令牌桶空了,请求就会被拒绝。

class TokenBucket:

def __init__(self, rate, capacity):

self.rate = rate # 每秒令牌数

self.capacity = 容量

self.tokens = 容量

self.last_refill = time.time()

def allow_request(self):

now = time.time()

elapsed = now – self.last_refill

self.tokens = min(self.容量, self.tokens + elapsed * self.rate)

self.last_refill = now

如果 self.tokens >= 1:

self.tokens -= 1

返回 True

返回 False

user_buckets = {}

def get_bucket(user_id):

如果 user_id 不在 user_buckets 中:

user_buckets[user_id] = TokenBucket(rate=1, capacity=60)

返回 user_buckets[user_id]。

def allow_request(user_id):

return get_bucket(user_id).allow_request()

漏桶

与令牌桶类似,但会以稳定的速度处理请求,多余的请求会被排队或丢弃。这非常适合需要严格流量控制的应用程序接口。

选择取决于您的使用情况:对于大多数应用程序接口来说,令牌桶或滑动窗口是兼顾灵活性和公平性的最佳选择。

键级速率限制

并非所有用户都是平等的,因此他们的限制也不应该是平等的。对每个 API 密钥、用户或 IP 地址实施速率限制,并为开发人员、付费客户或内部应用程序设置不同的层级。这可以很简单,为免费用户设置基本配额,为付费计划设置更高的阈值。

例如,在 Node.js/Express 中使用 express-rate-limit:

const rateLimit = require(‘express-rate-limit’);

const basicLimiter = rateLimit({

windowMs:1 * 60 * 1000, // 1 分钟

最大60,

keyGenerator: req => req.headers[‘x-api-key’] || req.ip、

消息:请求太多

});

const premiumLimiter = rateLimit({

窗口尺寸1 * 60 * 1000,

最大600,

keyGenerator: req => req.headers[‘x-api-key’] || req.ip、

消息:请求太多

});

// 根据用户类型有条件地应用限制器

app.use(‘/api’, (req, res, next) => {

如果 (isPremiumUser(req)){

return premiumLimiter(req, res, next);

}

return basicLimiter(req, res, next);

});

始终在响应标题中告知用户其剩余配额:

X-RateLimit-Limit: 60

X-RateLimit-Remaining: 10

X-RateLimit-Reset:1617576400

这种透明度可防止出现挫折感,并使开发人员能够在其应用程序中建立优美的回退策略。

基于资源的速率限制

对昂贵或敏感的端点(如文件上传、搜索查询或数据导出)实施更严格的控制非常重要。这些操作所需的服务器资源可能是基本 GET 请求的 10 倍或 100 倍。

根据端点实施细粒度限制:

  • /上传:每分钟 10 次请求
  • /搜索:每分钟 100 个请求
  • /read: 1000 个请求/分钟

这可以在网关或应用中间件中处理:

endpoint_limits = {

/upload’: (10, 60), # 每分钟 10 次

搜索”: (100, 60), # 每分钟 100 次

/read’: (1000, 60) # 每分钟 1000 次

}

def get_limit(path):

return ENDPOINT_LIMITS.get(path, (100, 60))

def allow_request(user_id, path):

max_requests, window = get_limit(path)

# 在每个端点应用滑动窗口逻辑

独立监控每个端点的使用情况。对反复触及限制或试图向多个端点 “喷射 “应用程序接口请求的用户发出警报或阻止其使用。

暂停和罚球

当用户超过速率限制时,决定是在设定时间内阻止进一步请求(”冷却”),还是只返回 429 响应,直到窗口重置。考虑使用动态阻止期限–对一次性违规者缩短阻止期限,对屡犯者延长阻止期限。

在快车道上:

res.status(429).set({

重试后”: 60 // 秒,直到他们可以再次尝试

}).json({ message: ‘Too many requests, please wait.’ });

对于持续滥用者,以指数方式增加拦截时间(指数回退)。记录所有此类事件,以便审计和审查。

3.动态速率限制

现代应用程序接口需要实时调整。如果服务器 CPU 达到 80%,则将所有限制降低 25%。如果错误率激增,则应加强控制,直到恢复稳定。动态限制可让您在保持稳定性能的同时,应对突发状况。

例如,使用 NGINX,您可以在 limit_req_zone 指令中使用变量,并即时更新共享内存限制。或者,在云环境中,使用 AWS API Gateway 的使用计划和 Lambda 授权器,根据观察到的指标调整配额。

对于自定义中间件,使用服务器指标:

如果 cpu_utilization > 0.8:

current_limit = max(int(base_limit * 0.75), min_limit)

否则

current_limit = base_limit

您甚至可以根据用户声誉、历史使用情况或订阅级别设置动态限制。

4.缓存作为限制速率的助推器

缓存是限速策略中的强大盟友。如果客户重复请求相同的数据,则提供缓存结果,而不是消耗后端资源。使用 Redis、Memcached 或 CDN 等工具缓存响应,避免不必要的点击。

一个使用 Redis 的简单 Python 示例:

导入 redis

cache = redis.Redis()

def get_data(key):

cached = cache.get(key)

如果已缓存:

返回缓存

# 昂贵的计算或数据库调用

data = expensive_operation(key)

cache.set(key, data, ex=60) # 缓存 60 秒

返回数据

确保为客户端也设置缓存头:

Cache-Control: public, max-age=60

ETag:”abcdef”

网关或边缘层的高速缓存可显著降低后端负载,平滑流量峰值。

5.应用程序接口网关和中间件

将大部分速率限制逻辑卸载到 API 网关或中间件上。Kong、Tyk、Zuplo 或 AWS API Gateway 等工具可提供开箱即用的速率限制、突发、分析和动态控制功能,从而将您的应用代码从这些问题中解放出来。

例如,与金刚在一起:

curl -i -X POST http://localhost:8001/services//plugins \

–data “name=速率限制

–data “config.minute=60″(数据

–数据 “config.policy=local”

这些平台提供

  • 全局速率限制和每个用户/每个终端配额
  • 实时指标和仪表板
  • 跨数据中心的分布式执行
  • 高级规则(例如,对某些端点或 IP 实施更严格的限制)

将应用程序与网关集成还能让更新变得更简单–只需更新配置即可更改限制,而无需部署代码。

6.监控、分析和警报

无法衡量就无法改进。为您的应用程序接口提供详细的速率限制决策日志–谁被节流、何时被节流、为什么被节流以及哪个端点受到了影响。汇总这些日志以进行趋势分析:

  • 哪些用户经常超出限制?
  • 是否有端点被滥用?
  • 最近的一次部署是否增加了错误率?

使用仪表盘和警报,在用户发现问题之前就发现问题。许多网关和 API 管理平台都提供内置分析功能。Grafana 等开源选项可以将数据库或日志文件中的指标可视化。

对于自动异常检测,可考虑使用机器学习模型或基于规则的触发器来标记异常峰值、登录失败或重复违反速率限制。

7.公平与缓冲区

公平使用至关重要,尤其是当用户为 API 访问付费时。为了避免让诚实的用户失望,可以考虑在首次超出限制时设置缓冲期或 “宽限期”–在强制执行之前允许小幅超出限制,或者在阻止之前发送一封警告邮件。

Python 中的逻辑示例:

如果 request_in_window > limit 且 requests_in_window <= limit + buffer:

# 警告用户,暂不阻止

send_warning(user_id)

elif requests_in_window > limit + buffer:

# 阻止用户

block_user(user_id)

提供自助服务仪表板,以便用户跟踪自己的使用情况、限制和重置时间。

8.安全考虑因素

速率限制不仅关系到性能。它也是抵御 DDoS 攻击、暴力攻击和刮擦的前沿防御手段。将速率限制与以下功能相结合

  • 验证码或屡试不爽的挑战-响应
  • 将可疑 IP 或用户代理列入黑名单
  • 更严格地限制敏感操作(如重置密码

对日志中的敏感元数据进行加密,并始终使用安全协议来限制通信速率。

9.应用程序接口管理平台

现代应用程序接口管理解决方案提供了满足您所有需求的单一窗口:速率限制、安全、分析、流量整形和开发人员体验。这些平台可以通过可配置的策略和最少的代码实现此处讨论的所有功能。

它们通常提供

  • 全球分布式执行(全球用户低延迟)
  • 可定制、按计划或按用户限制费率
  • 高级流量分析和报告
  • 与 CI/CD 管道集成,实现无缝配置
  • 安全审计和合规跟踪

如果您正在大规模运营或需要精细控制,强烈建议使用 API 管理平台。

10.持续审查和改进

最后,将您的速率限制策略视为一个有生命的系统。定期检查流量模式、端点受欢迎程度、错误日志和用户反馈。随着 API 的增长或业务需求的变化,更新您的限制、算法和执行策略。

运行游戏日和模拟攻击,在压力下测试您的策略。随时了解新的攻击技术和缓解技术。参与社区论坛并分享经验教训。

最后的话

API 速率限制是一门不断发展的学科,它结合了流量分析、算法执行、公平性和安全性。上述最佳实践将帮助您在 2026 年及以后实施稳健、自适应和用户友好的速率限制策略。

有了正确的方法,您就能保持 API 的响应速度和安全性,确保流畅的用户体验,同时保护您的基础架构不被滥用。随着形势的变化,请继续完善您的限制,利用最新的工具,并密切监控结果。正确的速率限制方法不仅是一种技术保障,也是一种业务促进因素,能让您的服务保持可靠,让您的客户在永远在线的数字世界中感到满意。

常见问题

什么是网络搜刮中的速率限制?

速率限制控制着每段时间内刮擦器发送请求的数量。它可以防止目标服务器不堪重负,降低检测风险,确保可持续的数据收集。大多数网站希望正常用户每秒发出 1-10 个请求。

如何确定合适的请求率?

保守起见,每 2-3 秒发出 1 个请求,然后在监控拦截的同时逐渐增加。检查 robots.txt 中的抓取延迟指令。企业网站可以承受更快的请求率,而小型网站则需要更慢的抓取速度。

什么是指数延迟,何时使用?

指数延迟会增加每次请求失败后的等待时间(1 秒、2 秒、4 秒、8 秒)。在收到 429 或 503 错误时使用,为服务器提供恢复时间。请求成功后重置延迟。

如何处理 API 速率限制?

监控速率限制标头(X-RateLimit-Remaining 和 X-RateLimit-Reset),在达到限制前暂停。对请求进行排队,使其保持在配额范围内。当超过限制时,执行带有回退功能的重试逻辑。

请求延迟是否应该随机化?

是的。1-5 秒之间的随机延迟比固定间隔更人性化。在基本延迟中添加抖动,以避免反僵尸系统检测到可预测的模式。根据页面类型和导航流程改变延迟时间。

如何在多个并发刮擦器间进行速率限制?

使用 Redis 或类似的共享存储进行集中式速率限制。实施令牌桶算法,将请求配额分配给不同的工作人员。协调刮擦器以避免意外超过组合限制。

如果忽略速率限制会发生什么?

忽略速率限制会导致 IP 屏蔽和验证码以及潜在的法律问题和永久禁止。咄咄逼人的搜刮行为会损害目标网站和搜刮社区的声誉。请始终以负责任的态度进行搜刮。

Leave a Comment

Required fields are marked *

A

You might also be interested in: